stk-code_catmod/src/guiengine/widgets/ribbon_widget.cpp
2019-09-21 11:25:13 +08:00

946 lines
34 KiB
C++

// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009-2015 Marianne Gagnon
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "guiengine/widgets/ribbon_widget.hpp"
#include <cmath>
#include "graphics/irr_driver.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/layout_manager.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/widgets/button_widget.hpp"
#include "input/input_manager.hpp"
#include "io/file_manager.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/string_utils.hpp"
#include "utils/vs.hpp"
#include <IGUIElement.h>
#include <IGUIEnvironment.h>
#include <IGUIButton.h>
using namespace GUIEngine;
using namespace irr::core;
using namespace irr::gui;
const char RibbonWidget::NO_ITEM_ID[] = "?";
// ----------------------------------------------------------------------------
RibbonWidget::RibbonWidget(const RibbonType type) : Widget(WTYPE_RIBBON)
{
for (unsigned int n=0; n<MAX_PLAYER_COUNT; n++)
{
m_selection[n] = -1;
}
m_selection[0] = 0; // only player 0 has a selection by default
m_ribbon_type = type;
m_mouse_focus = NULL;
m_listener = NULL;
m_check_inside_me = true;
m_supports_multiplayer = (type == RIBBON_TOOLBAR);
updateSelection();
} // RibbonWidget
// ----------------------------------------------------------------------------
RibbonWidget::~RibbonWidget()
{
m_active_children.clearWithoutDeleting();
} // ~RibbonWidget
// ----------------------------------------------------------------------------
void RibbonWidget::add()
{
assert(m_magic_number == 0xCAFEC001);
assert(m_x > -10.0f);
assert(m_y > -10.0f);
assert(m_w > 0.0f);
assert(m_h > 0.0f);
m_labels.clearWithoutDeleting();
rect<s32> widget_size = rect<s32>(m_x, m_y, m_x + m_w, m_y + m_h);
int id = (m_reserved_id == -1 ? getNewID() : m_reserved_id);
IGUIButton * btn = GUIEngine::getGUIEnv()->addButton(widget_size,
m_parent, id, L"");
m_element = btn;
m_active_children.clearWithoutDeleting(); // Is just a copy of m_children without the deactivated children. m_children takes care of memory.
for (unsigned int i=0; i<m_children.size(); i++)
{
if (m_children[i].isVisible())
{
m_active_children.push_back(m_children.get(i));
}
}
const int subbuttons_amount = m_active_children.size();
// For some ribbon types, we can have unequal sizes depending on whether
// items have labels or not
int with_label = 0;
int without_label = 0;
// ---- check how much space each child button will take and fit
// them within available space
int total_needed_space = 0;
for (int i=0; i<subbuttons_amount; i++)
{
// FIXME: why do I manually invoke the Layout Manager here?
LayoutManager::readCoords(m_active_children.get(i));
LayoutManager::applyCoords(m_active_children.get(i), NULL, this);
if (m_active_children[i].m_type != WTYPE_ICON_BUTTON &&
m_active_children[i].m_type != WTYPE_BUTTON)
{
Log::warn("RibbonWidget", "Ribbon widgets can only have "
"(icon)button widgets as children");
continue;
}
// ribbon children must not be keyboard navigatable, the parent
// ribbon takes care of that
if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
{
IconButtonWidget* icon = ((IconButtonWidget*)m_active_children.get(i));
icon->m_tab_stop = false;
}
bool has_label_underneath = m_active_children[i].m_text.size() > 0;
if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0)
{
has_label_underneath = false;
}
if (has_label_underneath) with_label++;
else without_label++;
total_needed_space += m_active_children[i].m_w;
}
//int biggest_y = 0;
const int button_y = 10;
const int one_button_width = (subbuttons_amount == 0 ? m_w :
int(roundf((float)m_w / (float)subbuttons_amount)));
const int one_button_height = (subbuttons_amount == 0 ? m_h :
int(roundf((float)m_h / (float)subbuttons_amount)));
int widget_x = -1;
int widget_y = -1;
// ---- add children
// TODO : the content of the ifs is way too large, separate functions would be better.
// Several pre-loop variables are used inside the ifs,
// so care must be taken to not break things
for (int i=0; i<subbuttons_amount; i++)
{
// ---- tab ribbons
if (getRibbonType() == RIBBON_TABS)
{
const int large_tab = (int)((with_label + without_label)
*one_button_width
/ (with_label + without_label/2.0f));
const int small_tab = large_tab/2;
stringw& message = m_active_children[i].m_text;
if (message.size() == 0)
{
if (widget_x == -1) widget_x = small_tab/2;
else widget_x += small_tab/2;
}
else
{
if (widget_x == -1) widget_x = large_tab/2;
else widget_x += large_tab/2;
}
IGUIButton * subbtn = NULL;
rect<s32> subsize = rect<s32>(widget_x - large_tab/2+2, 0,
widget_x + large_tab/2-2, m_h);
if (message.size() == 0)
{
subsize = rect<s32>(widget_x - small_tab/2+2, 0,
widget_x + small_tab/2-2, m_h);
}
if (m_active_children[i].m_type == WTYPE_BUTTON)
{
subbtn = GUIEngine::getGUIEnv()
->addButton(subsize, btn, getNewNoFocusID(),
message.c_str(), L"");
subbtn->setTabStop(false);
subbtn->setTabGroup(false);
if ((int)GUIEngine::getFont()->getDimension(message.c_str())
.Width > subsize.getWidth() &&
message.findFirst(L' ') == -1 &&
message.findFirst(L'\u00AD') == -1 )
{
// if message too long and contains no space and no soft
// hyphen, make the font smaller
subbtn->setOverrideFont(GUIEngine::getSmallFont());
}
}
else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
{
rect<s32> icon_part = rect<s32>(15,
0,
subsize.getHeight()+15,
subsize.getHeight());
if (message.size() == 0)
{
const int x = subsize.getWidth()/2 - subsize.getHeight()/2;
// no label, only icon, so center the icon
icon_part = rect<s32>(x,
0,
x + subsize.getHeight(),
subsize.getHeight());
}
// label at the *right* of the icon (for tabs)
rect<s32> label_part = rect<s32>(subsize.getHeight()+15,
0,
subsize.getWidth()-15,
subsize.getHeight());
// use the same ID for all subcomponents; since event handling
// is done per-ID, no matter which one your hover, this
// widget will get it
int same_id = getNewNoFocusID();
subbtn = GUIEngine::getGUIEnv()->addButton(subsize, btn,
same_id, L"", L"");
IGUIButton* icon =
GUIEngine::getGUIEnv()->addButton(icon_part, subbtn,
same_id, L"");
icon->setScaleImage(true);
std::string filename = GUIEngine::getSkin()->getThemedIcon(
m_active_children[i].m_properties[PROP_ICON]);
icon->setImage( irr_driver->getTexture(filename.c_str()) );
icon->setUseAlphaChannel(true);
icon->setDrawBorder(false);
icon->setTabStop(false);
IGUIStaticText* label =
GUIEngine::getGUIEnv()->addStaticText(message.c_str(),
label_part,
false /* border */,
true /* word wrap */,
subbtn, same_id);
if ((int)GUIEngine::getFont()->getDimension(message.c_str())
.Width > label_part.getWidth()&&
message.findFirst(L' ') == -1 &&
message.findFirst(L'\u00AD') == -1 )
{
// if message too long and contains no space and no soft
// hyphen, make the font smaller
label->setOverrideFont(GUIEngine::getSmallFont());
}
label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
label->setTabStop(false);
label->setNotClipped(true);
m_labels.push_back(label);
subbtn->setTabStop(false);
subbtn->setTabGroup(false);
}
else
{
Log::error("RibbonWidget", "Invalid tab bar contents");
}
m_active_children[i].m_element = subbtn;
if (message.size() == 0) widget_x += small_tab/2;
else widget_x += large_tab/2;
} // tabs
// ---- vertical tab ribbons
else if (getRibbonType() == RIBBON_VERTICAL_TABS)
{
const int tab_width = (int)((with_label + without_label)
*m_w
/ (with_label + without_label/2.0f));
stringw& message = m_active_children[i].m_text;
widget_x = tab_width/2;
if (widget_y == -1)
widget_y = 0;
else
widget_y += one_button_height;
IGUIButton * subbtn = NULL;
// The buttons will overlap without this,
// as drawBoxFromStretchableTexture draw the borders outside
// of a widget's reserved area
int VERT_BORDER_MARGIN = 8;
rect<s32> subbtn_rec = rect<s32>(widget_x - tab_width/2+2, widget_y + VERT_BORDER_MARGIN,
widget_x + tab_width/2-2, widget_y - VERT_BORDER_MARGIN + one_button_height);
// TODO Add support for BUTTON type when needed
if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
{
// The icon will take 1/3rd of the tab width at most, less if height is lacking
int icon_size = std::min(m_w/3, subbtn_rec.getHeight()+2*VERT_BORDER_MARGIN);
rect<s32> icon_part = rect<s32>(5,
one_button_height/2 - icon_size/2-VERT_BORDER_MARGIN,
icon_size+5,
one_button_height/2 + icon_size/2-VERT_BORDER_MARGIN);
// label at the *right* of the icon (for tabs)
rect<s32> label_part = rect<s32>(icon_size+5,
5-VERT_BORDER_MARGIN,
subbtn_rec.getWidth()-5,
one_button_height-5-VERT_BORDER_MARGIN);
// use the same ID for all subcomponents; since event handling
// is done per-ID, no matter which one your hover, this
// widget will get it
int same_id = getNewNoFocusID();
subbtn = GUIEngine::getGUIEnv()->addButton(subbtn_rec, btn,
same_id, L"", L"");
IGUIButton* icon =
GUIEngine::getGUIEnv()->addButton(icon_part, subbtn,
same_id, L"");
icon->setScaleImage(true);
std::string filename = GUIEngine::getSkin()->getThemedIcon(
m_active_children[i].m_properties[PROP_ICON]);
icon->setImage( irr_driver->getTexture(filename.c_str()) );
icon->setUseAlphaChannel(true);
icon->setDrawBorder(false);
icon->setTabStop(false);
IGUIStaticText* label =
GUIEngine::getGUIEnv()->addStaticText(message.c_str(),
label_part,
false /* border */,
true /* word wrap */,
subbtn, same_id);
if ((int)GUIEngine::getFont()->getDimension(message.c_str())
.Width > label_part.getWidth()&&
message.findFirst(L' ') == -1 &&
message.findFirst(L'\u00AD') == -1 )
{
// if message too long and contains no space and no soft
// hyphen, make the font smaller
label->setOverrideFont(GUIEngine::getSmallFont());
}
label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
label->setTabStop(false);
label->setNotClipped(true);
m_labels.push_back(label);
subbtn->setTabStop(false);
subbtn->setTabGroup(false);
}
else
{
Log::error("RibbonWidget", "Invalid tab bar contents");
}
m_active_children[i].m_element = subbtn;
} // vertical-tabs
// ---- icon ribbons
else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
{
if (widget_x == -1) widget_x = one_button_width/2;
// find how much space to keep for the label under the button.
// consider font size, whether the label is multiline, etc...
bool has_label = m_active_children[i].m_text.size() > 0;
if (m_active_children[i].m_properties[PROP_LABELS_LOCATION].size() > 0)
{
has_label = false;
}
const int needed_space_under_button = has_label
? GUIEngine::getFontHeight()
: 10;
float imageRatio =
(float)m_active_children[i].m_w / (float)m_active_children[i].m_h;
// calculate the size of the image
std::string filename =
GUIEngine::getSkin()->getThemedIcon(m_active_children[i].m_properties[PROP_ICON]);
video::ITexture* image =
irr_driver->getTexture((filename).c_str());
if(!image)
{
std::string file = file_manager->getAsset(FileManager::GUI_ICON,"main_help.png");
image = irr_driver->getTexture(file);
if(!image)
Log::fatal("RibbonWidget",
"Can't find fallback texture 'gui/icons/main_help.png, aborting.");
}
float image_h = (float)image->getSize().Height;
float image_w = image_h*imageRatio;
float zoom = (float) (m_h - button_y - needed_space_under_button) / image_h;
float zoom_x = (float) one_button_width / image_w;
if(zoom_x < zoom)
zoom = zoom_x;
// ---- add bitmap button part
// backup and restore position in case the same object is added
// multiple times (FIXME: unclean)
int old_x = m_active_children[i].m_x;
int old_y = m_active_children[i].m_y;
int old_w = m_active_children[i].m_w;
int old_h = m_active_children[i].m_h;
m_active_children[i].m_x = widget_x - int(image_w*zoom/2.0f);
m_active_children[i].m_y = button_y;
m_active_children[i].m_w = int(image_w*zoom);
m_active_children[i].m_h = int(image_h*zoom);
IconButtonWidget* icon = ((IconButtonWidget*)m_active_children.get(i));
if (icon->m_properties[PROP_EXTEND_LABEL].size() == 0)
{
icon->m_properties[PROP_EXTEND_LABEL] =
StringUtils::toString(one_button_width - icon->m_w);
}
m_active_children.get(i)->m_parent = btn;
m_active_children.get(i)->add();
// restore backuped size and location (see above for more info)
m_active_children[i].m_x = old_x;
m_active_children[i].m_y = old_y;
m_active_children[i].m_w = old_w;
m_active_children[i].m_h = old_h;
// the label itself will be added by the icon widget. since it
// adds the label outside of the widget area it is assigned to,
// the label will appear in the area we want at the bottom
widget_x += one_button_width;
}
else
{
Log::warn("RiggonWidget", "Invalid contents type in ribbon");
}
//m_children[i].id = subbtn->getID();
m_active_children[i].m_event_handler = this;
}// next sub-button
id = m_element->getID();
m_element->setTabOrder(id);
m_element->setTabGroup(false);
updateSelection();
if (!m_is_visible)
setVisible(false);
} // add
// ----------------------------------------------------------------------------
void RibbonWidget::addTextChild(const core::stringw& text, const std::string &id)
{
// This method should only be called BEFORE a widget is added
assert(m_element == NULL);
ButtonWidget* item = new ButtonWidget();
item->m_text = text;
item->m_properties[PROP_ID] = id;
m_children.push_back(item);
} // addTextChild
// ----------------------------------------------------------------------------
void RibbonWidget::addIconChild(const core::stringw& text, const std::string &id,
const int w, const int h,
const std::string &icon,
const IconButtonWidget::IconPathType icon_path_type)
{
// This method should only be called BEFORE a widget is added
assert(m_element == NULL);
IconButtonWidget* ribbon_item = new IconButtonWidget();
ribbon_item->m_properties[PROP_ID] = id;
ribbon_item->setImage(icon.c_str(), icon_path_type);
ribbon_item->m_properties[PROP_WIDTH] = StringUtils::toString(w);
ribbon_item->m_properties[PROP_HEIGHT] = StringUtils::toString(h);
ribbon_item->m_text = text;
m_children.push_back(ribbon_item);
} // addIconChild
// ----------------------------------------------------------------------------
void RibbonWidget::clearAllChildren()
{
// This method should only be called BEFORE a widget is added
assert(m_element == NULL);
m_children.clearAndDeleteAll();
} // clearAllChildren
// ----------------------------------------------------------------------------
void RibbonWidget::removeChildNamed(const char* name)
{
// This method should only be called BEFORE a widget is added
assert(m_element == NULL);
for (Widget* child : m_children)
{
if (child->m_properties[PROP_ID] == name)
{
m_children.erase(child);
return;
}
}
}
// ----------------------------------------------------------------------------
void RibbonWidget::select(std::string item, const int mousePlayerID)
{
const int subbuttons_amount = m_active_children.size();
for (int i=0; i<subbuttons_amount; i++)
{
if (m_active_children[i].m_properties[PROP_ID] == item)
{
m_selection[mousePlayerID] = i;
updateSelection();
return;
}
}
} // select
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::rightPressed(const int playerID)
{
return moveToNextItem(/*horizontal*/ true, /*reverse*/ false, playerID);
} // rightPressed
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::leftPressed(const int playerID)
{
return moveToNextItem(/*horizontal*/ true, /*reverse*/ true, playerID);
} // leftPressed
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::downPressed(const int playerID)
{
return moveToNextItem(/*horizontal*/ false, /*reverse*/ false, playerID);
} // downPressed
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::upPressed(const int playerID)
{
return moveToNextItem(/*horizontal*/ false, /*reverse*/ true, playerID);
} // upPressed
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::moveToNextItem(const bool horizontally, const bool reverse, const int playerID)
{
EventPropagation result = propagationType(horizontally);
// Do nothing and do not block navigating out of the widget
if (result == EVENT_BLOCK) return result;
int old_selection = m_selection[playerID];
selectNextActiveWidget(horizontally, reverse, playerID, old_selection);
if (m_selection[playerID] == old_selection && !horizontally)
return EVENT_BLOCK;
updateSelection();
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{
const int mousePlayerID = input_manager->getPlayerKeyboardID();
if (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER)
{
m_mouse_focus = m_active_children.get(m_selection[playerID]);
}
}
// if we reached a filler item, move again (but don't wrap)
if (getSelectionIDString(playerID) == RibbonWidget::NO_ITEM_ID)
{
if (((m_selection[playerID] > 0) && reverse ) ||
((m_selection[playerID] + 1 < int(m_active_children.size()))&& !reverse) )
{
moveToNextItem(horizontally, reverse, playerID);
}
}
return result;
} // moveToNextItem
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::propagationType(const bool horizontally)
{
EventPropagation result;
if (horizontally)
{
result = m_ribbon_type == RIBBON_VERTICAL_TABS ? EVENT_BLOCK :
m_ribbon_type != RIBBON_TOOLBAR ? EVENT_LET :
EVENT_BLOCK_BUT_HANDLED;
}
else
{
result = m_ribbon_type != RIBBON_VERTICAL_TABS ? EVENT_BLOCK :
EVENT_LET;
}
if (m_deactivated) result = EVENT_BLOCK;
// empty ribbon, or only one item (can't move)
if (m_active_children.size() < 2) result = EVENT_BLOCK;
return result;
}
/**
* Move to the next child widget in the requested direction.
* If it is inactive, move again, until it finds an activated child or test all childs
*/
void RibbonWidget::selectNextActiveWidget(const bool horizontally, const bool reverse,
const int playerID, const int old_selection)
{
int loop_counter = 0;
do
{
if (reverse)
m_selection[playerID]--;
else
m_selection[playerID]++;
if (m_selection[playerID] >= int(m_active_children.size()) || m_selection[playerID] < 0)
{
// In vertical tabs, don't loop when reaching the top or bottom
if (!horizontally)
{
if (reverse)
m_selection[playerID] = old_selection;
else
m_selection[playerID] = old_selection;
return;
}
bool left = (m_selection[playerID] < 0);
if (m_listener != NULL) m_listener->onRibbonWidgetScroll(left ? -1 : 1);
bool select_zero = (m_event_handler && left) || (!m_event_handler && !left);
m_selection[playerID] = select_zero ? 0 : m_active_children.size()-1;
}
loop_counter++;
if (loop_counter > (int)m_active_children.size())
{
Log::warn("RibbonWidget", "All the buttons of the focused ribbon"
" are deactivated !");
break;
}
} while (!m_active_children.get(m_selection[playerID])->isActivated());
}
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::focused(const int playerID)
{
Widget::focused(playerID);
if (m_active_children.size() < 1) return EVENT_LET; // empty ribbon
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{
const int mousePlayerID = input_manager->getPlayerKeyboardID();
if (m_mouse_focus == NULL && m_selection[playerID] != -1 &&
(playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER))
{
if (m_selection[playerID] < int(m_active_children.size()))
{
m_mouse_focus = m_active_children.get(m_selection[playerID]);
m_mouse_focus->focused(playerID);
}
}
}
else
{
if (m_selection[playerID] != -1)
{
int selection = m_selection[playerID];
if (selection < (int)m_active_children.size())
m_active_children.get(selection)->focused(playerID);
}
}
if (m_listener != NULL) m_listener->onRibbonWidgetFocus( this, playerID );
return EVENT_LET;
} // focused
// ----------------------------------------------------------------------------
void RibbonWidget::unfocused(const int playerID, Widget* new_focus)
{
if (new_focus != NULL && new_focus != this && !m_active_children.contains(new_focus))
{
if (m_selection[playerID] >= 0 && m_selection[playerID] < int(m_active_children.size()))
{
m_active_children.get(m_selection[playerID])->unfocused(playerID, new_focus);
}
}
//if (m_selection[0] != -1) m_children.get(m_selection[0])->unfocused(0);
} // unfocused
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::mouseHovered(Widget* child,
const int mousePlayerID)
{
if (m_deactivated) return EVENT_LET;
const int subbuttons_amount = m_active_children.size();
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{
//Log::info("RibbonWidget", "Setting m_mouse_focus");
m_mouse_focus = child;
}
// In toolbar ribbons, hovering selects
if (m_ribbon_type == RIBBON_TOOLBAR)
{
for (int i=0; i<subbuttons_amount; i++)
{
if (m_active_children.get(i) == child)
{
// Was already selected, don't send another event
if (m_selection[mousePlayerID] == i) return EVENT_BLOCK;
// Don't change selection on hover for others
m_selection[mousePlayerID] = i;
break;
}
}
}
updateSelection();
return EVENT_BLOCK;
} // mouseHovered
EventPropagation RibbonWidget::onClick()
{
if (m_ribbon_type == RIBBON_TOOLBAR)
return EVENT_BLOCK;
return EVENT_LET;
}
// ----------------------------------------------------------------------------
const std::string& RibbonWidget::getSelectionIDString(const int playerID)
{
static std::string empty;
if (m_selection[playerID] == -1) return empty;
if (m_active_children.size() == 0) return empty;
// This can happen if an addon is removed, which causes a tab group
// to be removed. If this tab group was previously selected, an
// invalid array element would be accessed. In this case just pretend
// that the first child was selected previously.
if(m_selection[playerID]>=(int)m_active_children.size())
return m_active_children[0].m_properties[PROP_ID];
return m_active_children[m_selection[playerID]].m_properties[PROP_ID];
} // getSelectionIDString
// ----------------------------------------------------------------------------
void RibbonWidget::updateSelection()
{
const int subbuttons_amount = m_active_children.size();
// FIXME: m_selection, m_selected, m_mouse_focus... what a mess...
// Update selection flags for mouse player
for (unsigned int p=0; p<MAX_PLAYER_COUNT; p++)
{
for (int i=0; i<subbuttons_amount; i++)
{
bool new_val = (i == m_selection[p]);
if (!new_val && m_active_children[i].m_selected[p])
{
m_active_children[i].unfocused(PLAYER_ID_GAME_MASTER, NULL);
}
m_active_children[i].m_selected[p] = new_val;
if (new_val) m_active_children[i].focused(PLAYER_ID_GAME_MASTER);
}
}
if (m_listener) m_listener->onSelectionChange();
} // updateSelection
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::onActivationInput(const int playerID)
{
assert(m_magic_number == 0xCAFEC001);
if (m_deactivated)
return EVENT_BLOCK;
if (m_selection[playerID] > -1 &&
m_selection[playerID] < (int)(m_active_children.size()))
{
if (m_active_children[m_selection[playerID]].m_deactivated)
{
return EVENT_BLOCK;
}
}
return EVENT_LET;
}
// ----------------------------------------------------------------------------
EventPropagation RibbonWidget::transmitEvent(Widget* w,
const std::string& originator,
const int playerID)
{
assert(m_magic_number == 0xCAFEC001);
if (!m_deactivated)
{
const int subbuttons_amount = m_active_children.size();
for (int i=0; i<subbuttons_amount; i++)
{
if (m_active_children[i].m_properties[PROP_ID] == originator)
{
m_selection[playerID] = i;
break;
}
}
updateSelection();
}
// bring focus back to enclosing ribbon widget
this->setFocusForPlayer( playerID );
if (m_selection[playerID] > -1 &&
m_selection[playerID] < (int)(m_active_children.size()))
{
if (m_active_children[m_selection[playerID]].m_deactivated)
{
GUIEngine::getCurrentScreen()->onDisabledItemClicked(
m_active_children[m_selection[playerID]].m_properties[PROP_ID]);
return EVENT_BLOCK;
}
}
return EVENT_LET;
} // transmitEvent
// ----------------------------------------------------------------------------
void RibbonWidget::setLabel(const unsigned int id, irr::core::stringw new_name)
{
if (m_element == NULL)
{
// before adding
m_children[id].setText(new_name);
}
else
{
// after adding
// ignore this call for ribbons without labels
if (m_labels.size() == 0) return;
assert(id < m_labels.size());
m_labels[id].setText(new_name.c_str());
//m_text = new_name;
}
} // setLabel
// ----------------------------------------------------------------------------
void RibbonWidget::setItemVisible(const unsigned int id, bool visible)
{
m_children[id].setVisible(visible);
// ignore this call for ribbons without labels
if (m_labels.size() == 0) return;
m_labels[id].setVisible(visible);
} // RibbonWidget
// ----------------------------------------------------------------------------
int RibbonWidget::findItemNamed(const char* internalName)
{
const int size = m_children.size();
for (int n=0; n<size; n++)
{
if (m_children[n].m_properties[PROP_ID] == internalName) return n;
}
return -1;
} // findItemNamed
// ----------------------------------------------------------------------------
Widget* RibbonWidget::findWidgetNamed(const char* internalName)
{
int id = findItemNamed(internalName);
if (id >= 0)
return m_children.get(id);
return NULL;
} // findItemNamed