Add vertical tabs (#3314)

* Add vertical tabs to ribbon widget types

* Update GUI engine for vertical tabs

* Add the ability to get a ribbon's active children number

* Add full looping for vertical tabs
This commit is contained in:
Alayan-stk-2 2018-06-20 03:00:10 +02:00 committed by auriamg
parent 750320fbeb
commit be98a6c4c3
6 changed files with 307 additions and 86 deletions

View File

@ -99,7 +99,8 @@ namespace GUIEngine
\n \n
\subsection widget1 WTYPE_RIBBON \subsection widget1 WTYPE_RIBBON
<em> Names in XML files: </em> \c "ribbon", \c "buttonbar", \c "tabs" <em> Names in XML files: </em> \c "ribbon", \c "buttonbar", \c "tabs",
\c"vertical-tabs"
Appears as an horizontal bar containing elements laid in a row, each being Appears as an horizontal bar containing elements laid in a row, each being
and icon and/or a label and icon and/or a label
@ -351,9 +352,9 @@ namespace GUIEngine
size (parent size means the parent \<div\> or the whole screen if none). A size (parent size means the parent \<div\> or the whole screen if none). A
negative value can also be passed to start coordinate from right and/or negative value can also be passed to start coordinate from right and/or
bottom, instead of starting from top-left corner as usual. bottom, instead of starting from top-left corner as usual.
Note that in many cases, it is not necessary to manually a position. Div Note that in many cases, it is not necessary to manually set a position. Div
layouts will often manage that for you (see PROP_LAYOUT). Other widgets will layouts will often manage that for you (see PROP_LAYOUT). Other widgets will
also automativally manage the position and size of their children, for also automatically manage the position and size of their children, for
instance ribbons. instance ribbons.
\n \n
@ -367,7 +368,7 @@ namespace GUIEngine
Note that in many cases, it is not necessary to manually a size. Div layouts Note that in many cases, it is not necessary to manually a size. Div layouts
will often manage that for you (see PROP_LAYOUT). In addition, sizes are will often manage that for you (see PROP_LAYOUT). In addition, sizes are
automatically calculated for widgets made of icons and/or text like labels automatically calculated for widgets made of icons and/or text like labels
and plain icons. Other widgets will also automativally manage the position and plain icons. Other widgets will also automatically manage the position
and size of their children, for instance ribbons. and size of their children, for instance ribbons.
Another possible value is "fit", which will make a \<div\> fit to its Another possible value is "fit", which will make a \<div\> fit to its

View File

@ -433,12 +433,12 @@ void EventHandler::sendNavigationEvent(const NavigationDirection nav, const int
else if (nav == NAV_DOWN) else if (nav == NAV_DOWN)
propagation_state = widget_to_call->downPressed(playerID); propagation_state = widget_to_call->downPressed(playerID);
if (propagation_state == EVENT_LET)
sendEventToUser(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID);
if (propagation_state == EVENT_LET || propagation_state == EVENT_BLOCK_BUT_HANDLED) if (propagation_state == EVENT_LET || propagation_state == EVENT_BLOCK_BUT_HANDLED)
handled_by_widget = true; handled_by_widget = true;
if (propagation_state == EVENT_LET)
sendEventToUser(widget_to_call, widget_to_call->m_properties[PROP_ID], playerID);
if (widget_to_call->m_event_handler == NULL) if (widget_to_call->m_event_handler == NULL)
break; break;
@ -476,6 +476,20 @@ void EventHandler::navigate(const NavigationDirection nav, const int playerID)
assert(list != NULL); assert(list != NULL);
list->setSelectionID(nav == NAV_UP ? list->getItemCount() - 1 : 0); list->setSelectionID(nav == NAV_UP ? list->getItemCount() - 1 : 0);
} }
// Similar exception for vertical tabs, only apply when entering with down/up
if (closest_widget->m_type == GUIEngine::WTYPE_RIBBON && (nav == NAV_UP || nav == NAV_DOWN))
{
RibbonWidget* ribbon = dynamic_cast<RibbonWidget*>(closest_widget);
assert(ribbon != NULL);
if (ribbon->getRibbonType() == GUIEngine::RibbonType::RIBBON_VERTICAL_TABS)
{
int new_selection = (nav == NAV_UP) ?
ribbon->getActiveChildrenNumber(playerID) - 1 : 0;
ribbon->setSelection(new_selection, playerID);
// The tab selection triggers an action
sendEventToUser(ribbon, ribbon->m_properties[PROP_ID], playerID);
}
}
} }
return; return;
@ -535,6 +549,7 @@ int EventHandler::findIDClosestWidget(const NavigationDirection nav, const int p
} }
int offset = 0; int offset = 0;
int rightmost = w_test->m_x + w_test->m_w;
if (nav == NAV_UP || nav == NAV_DOWN) if (nav == NAV_UP || nav == NAV_DOWN)
{ {
@ -564,7 +579,7 @@ int EventHandler::findIDClosestWidget(const NavigationDirection nav, const int p
// we substract the smaller from the bigger // we substract the smaller from the bigger
// else, the smaller is 0 and we keep the bigger // else, the smaller is 0 and we keep the bigger
int right_offset = std::max(0, w_test->m_x - w->m_x); int right_offset = std::max(0, w_test->m_x - w->m_x);
int left_offset = std::max(0, (w->m_x + w->m_w) - (w_test->m_x + w_test->m_w)); int left_offset = std::max(0, (w->m_x + w->m_w) - rightmost);
offset = std::max (right_offset - left_offset, left_offset - right_offset); offset = std::max (right_offset - left_offset, left_offset - right_offset);
} }
else if (nav == NAV_LEFT || nav == NAV_RIGHT) else if (nav == NAV_LEFT || nav == NAV_RIGHT)
@ -572,7 +587,7 @@ int EventHandler::findIDClosestWidget(const NavigationDirection nav, const int p
if (nav == NAV_LEFT) if (nav == NAV_LEFT)
{ {
// compare current leftmost point with other widget rightmost // compare current leftmost point with other widget rightmost
distance = w->m_x - (w_test->m_x + w_test->m_w); distance = w->m_x - rightmost;
} }
else else
{ {
@ -585,9 +600,28 @@ int EventHandler::findIDClosestWidget(const NavigationDirection nav, const int p
int up_offset = std::max(0, (w->m_y + w->m_h) - (w_test->m_y + w_test->m_h)); int up_offset = std::max(0, (w->m_y + w->m_h) - (w_test->m_y + w_test->m_h));
offset = std::max (down_offset - up_offset, up_offset - down_offset); offset = std::max (down_offset - up_offset, up_offset - down_offset);
// Special case for vertical tabs : select the top element of the body
if (w->m_type == GUIEngine::WTYPE_RIBBON)
{
RibbonWidget* ribbon = dynamic_cast<RibbonWidget*>(w);
if (ribbon->getRibbonType() == GUIEngine::RibbonType::RIBBON_VERTICAL_TABS)
{
offset = w_test->m_y;
offset *= 100;
// Don't count elements above the tabs or too high as valid
if (!(w_test->m_x > (w->m_x + w->m_w) || rightmost < w->m_x) ||
(w_test->m_y + w_test->m_h) < w->m_y)
{
distance = BIG_DISTANCE;
wrapping_distance = BIG_DISTANCE;
}
}
}
// No lateral selection if there is not at least partial alignement // No lateral selection if there is not at least partial alignement
// >= because we don't want it to trigger if two widgets touch each other // >= because we don't want it to trigger if two widgets touch each other
if (offset >= w->m_h) else if (offset >= w->m_h)
{ {
distance = BIG_DISTANCE; distance = BIG_DISTANCE;
wrapping_distance = BIG_DISTANCE; wrapping_distance = BIG_DISTANCE;
@ -912,4 +946,3 @@ EventPropagation EventHandler::onGUIEvent(const SEvent& event)
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -102,6 +102,10 @@ void Screen::parseScreenFileDiv(irr::io::IXMLReader* xml, PtrVector<Widget>& app
{ {
append_to.push_back(new RibbonWidget(RIBBON_TABS)); append_to.push_back(new RibbonWidget(RIBBON_TABS));
} }
else if (wcscmp(L"vertical-tabs", xml->getNodeName()) == 0)
{
append_to.push_back(new RibbonWidget(RIBBON_VERTICAL_TABS));
}
else if (wcscmp(L"spinner", xml->getNodeName()) == 0) else if (wcscmp(L"spinner", xml->getNodeName()) == 0)
{ {
append_to.push_back(new SpinnerWidget()); append_to.push_back(new SpinnerWidget());
@ -270,9 +274,10 @@ if(prop_name != NULL) widget.m_properties[prop_flag] = core::stringc(prop_name).
// We're done parsing this 'ribbon', return one step back in // We're done parsing this 'ribbon', return one step back in
// the recursive call. // the recursive call.
if (wcscmp(L"ribbon", xml->getNodeName()) == 0 || if (wcscmp(L"ribbon", xml->getNodeName()) == 0 ||
wcscmp(L"buttonbar", xml->getNodeName()) == 0 || wcscmp(L"buttonbar", xml->getNodeName()) == 0 ||
wcscmp(L"tabs", xml->getNodeName()) == 0) wcscmp(L"tabs", xml->getNodeName()) == 0 ||
wcscmp(L"vertical-tabs", xml->getNodeName()) == 0)
return; return;
} }
break; break;

View File

@ -1003,6 +1003,56 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
material2D.UseMipMaps = true; material2D.UseMipMaps = true;
} }
} }
/* vertical tab-bar ribbons */
else if (type == RIBBON_VERTICAL_TABS)
{
video::SMaterial& material2D =
irr_driver->getVideoDriver()->getMaterial2D();
for (unsigned int n=0; n<MATERIAL_MAX_TEXTURES; n++)
{
material2D.UseMipMaps = false;
}
const bool mouseIn = rect.isPointInside(irr_driver->getDevice()
->getCursorControl()
->getPosition() );
BoxRenderParams* params;
if (mark_selected && (focused || parent_focused))
params = &SkinConfig::m_render_params["verticalTab::focused"];
else if (parentRibbon->m_mouse_focus == widget && mouseIn)
params = &SkinConfig::m_render_params["verticalTab::focused"];
else if (mark_selected)
params = &SkinConfig::m_render_params["verticalTab::down"];
else
params = &SkinConfig::m_render_params["verticalTab::neutral"];
// automatically guess from position on-screen if tabs go left or right
unsigned int screen_width = irr_driver->getActualScreenSize().Width;
const bool horizontal_flip =
(unsigned int)rect.UpperLeftCorner.X > screen_width/ 2;
params->m_vertical_flip = false;
core::recti rect2 = rect;
if (mark_selected)
{
// the selected tab should be slighlty bigger than others
if (horizontal_flip) rect2.UpperLeftCorner.X -= screen_width/50;
else rect2.LowerRightCorner.X += screen_width/50;
}
drawBoxFromStretchableTexture(widget, rect2, *params,
parentRibbon->m_deactivated ||
widget->m_deactivated);
for (unsigned int n=0; n<MATERIAL_MAX_TEXTURES; n++)
{
material2D.UseMipMaps = true;
}
}
/* icon ribbons */ /* icon ribbons */
else else
{ {

View File

@ -115,7 +115,7 @@ void RibbonWidget::add()
if (m_active_children[i].m_type != WTYPE_ICON_BUTTON && if (m_active_children[i].m_type != WTYPE_ICON_BUTTON &&
m_active_children[i].m_type != WTYPE_BUTTON) m_active_children[i].m_type != WTYPE_BUTTON)
{ {
Log::warn("RiggonWidget", "Ribbon widgets can only have " Log::warn("RibbonWidget", "Ribbon widgets can only have "
"(icon)button widgets as children"); "(icon)button widgets as children");
continue; continue;
} }
@ -144,19 +144,26 @@ void RibbonWidget::add()
//int biggest_y = 0; //int biggest_y = 0;
const int button_y = 10; const int button_y = 10;
const int one_button_space = (subbuttons_amount == 0 ? m_w : const int one_button_width = (subbuttons_amount == 0 ? m_w :
int(roundf((float)m_w / (float)subbuttons_amount))); 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_x = -1;
int widget_y = -1;
// ---- add children // ---- 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++) for (int i=0; i<subbuttons_amount; i++)
{ {
// ---- tab ribbons // ---- tab ribbons
if (getRibbonType() == RIBBON_TABS) if (getRibbonType() == RIBBON_TABS)
{ {
const int large_tab = (int)((with_label + without_label) const int large_tab = (int)((with_label + without_label)
*one_button_space *one_button_width
/ (with_label + without_label/2.0f)); / (with_label + without_label/2.0f));
const int small_tab = large_tab/2; const int small_tab = large_tab/2;
@ -277,11 +284,108 @@ void RibbonWidget::add()
if (message.size() == 0) widget_x += small_tab/2; if (message.size() == 0) widget_x += small_tab/2;
else widget_x += large_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 = file_manager->getAsset(
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);
label->setRightToLeft(translations->isRTLText(message));
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 // ---- icon ribbons
else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON) else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON)
{ {
if (widget_x == -1) widget_x = one_button_space/2; if (widget_x == -1) widget_x = one_button_width/2;
// find how much space to keep for the label under the button. // find how much space to keep for the label under the button.
// consider font size, whether the label is multiline, etc... // consider font size, whether the label is multiline, etc...
@ -316,7 +420,7 @@ void RibbonWidget::add()
float image_h = (float)image->getSize().Height; float image_h = (float)image->getSize().Height;
float image_w = image_h*imageRatio; float image_w = image_h*imageRatio;
float zoom = (float) (m_h - button_y - needed_space_under_button) / image_h; float zoom = (float) (m_h - button_y - needed_space_under_button) / image_h;
float zoom_x = (float) one_button_space / image_w; float zoom_x = (float) one_button_width / image_w;
if(zoom_x < zoom) if(zoom_x < zoom)
zoom = zoom_x; zoom = zoom_x;
@ -338,7 +442,7 @@ void RibbonWidget::add()
if (icon->m_properties[PROP_EXTEND_LABEL].size() == 0) if (icon->m_properties[PROP_EXTEND_LABEL].size() == 0)
{ {
icon->m_properties[PROP_EXTEND_LABEL] = icon->m_properties[PROP_EXTEND_LABEL] =
StringUtils::toString(one_button_space - icon->m_w); StringUtils::toString(one_button_width - icon->m_w);
} }
m_active_children.get(i)->m_parent = btn; m_active_children.get(i)->m_parent = btn;
@ -354,7 +458,7 @@ void RibbonWidget::add()
// adds the label outside of the widget area it is assigned to, // adds the label outside of the widget area it is assigned to,
// the label will appear in the area we want at the bottom // the label will appear in the area we want at the bottom
widget_x += one_button_space; widget_x += one_button_width;
} }
else else
{ {
@ -373,7 +477,7 @@ void RibbonWidget::add()
if (!m_is_visible) if (!m_is_visible)
setVisible(false); setVisible(false);
} // add } // add
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -460,65 +564,64 @@ void RibbonWidget::select(std::string item, const int mousePlayerID)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
EventPropagation RibbonWidget::rightPressed(const int playerID) EventPropagation RibbonWidget::rightPressed(const int playerID)
{ {
EventPropagation result = m_ribbon_type != RIBBON_TOOLBAR ? EVENT_LET : EVENT_BLOCK_BUT_HANDLED; return moveToNextItem(/*horizontal*/ true, /*reverse*/ false, playerID);
if (m_deactivated) return result;
// empty ribbon, or only one item (can't move right)
if (m_active_children.size() < 2) return result;
m_selection[playerID]++;
if (m_selection[playerID] >= int(m_active_children.size()))
{
if (m_listener != NULL) m_listener->onRibbonWidgetScroll(1);
m_selection[playerID] = m_event_handler ? m_active_children.size()-1 : 0;
}
updateSelection();
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_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] + 1 < int(m_active_children.size()))
{
rightPressed(playerID);
}
}
return result;
} // rightPressed } // rightPressed
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
EventPropagation RibbonWidget::leftPressed(const int playerID) EventPropagation RibbonWidget::leftPressed(const int playerID)
{ {
EventPropagation result = m_ribbon_type != RIBBON_TOOLBAR ? EVENT_LET : EVENT_BLOCK_BUT_HANDLED; return moveToNextItem(/*horizontal*/ true, /*reverse*/ true, playerID);
} // leftPressed
if (m_deactivated) return result;
// empty ribbon, or only one item (can't move left)
if (m_active_children.size() < 2) return result;
m_selection[playerID]--; // ----------------------------------------------------------------------------
if (m_selection[playerID] < 0) 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;
if (reverse)
m_selection[playerID]--;
else
m_selection[playerID]++;
if (m_selection[playerID] >= int(m_active_children.size()) || m_selection[playerID] < 0)
{ {
if (m_listener != NULL) m_listener->onRibbonWidgetScroll(-1); // In vertical tabs, don't loop when reaching the top or bottom
if (!horizontally)
{
if (reverse)
m_selection[playerID]++;
else
m_selection[playerID]--;
m_selection[playerID] = m_event_handler return EVENT_BLOCK;
? 0 }
: m_active_children.size()-1; 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;
} }
updateSelection(); updateSelection();
if (m_ribbon_type == RIBBON_COMBO) if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{ {
const int mousePlayerID = input_manager->getPlayerKeyboardID(); const int mousePlayerID = input_manager->getPlayerKeyboardID();
if (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER) if (playerID == mousePlayerID || playerID == PLAYER_ID_GAME_MASTER)
@ -530,16 +633,38 @@ EventPropagation RibbonWidget::leftPressed(const int playerID)
// if we reached a filler item, move again (but don't wrap) // if we reached a filler item, move again (but don't wrap)
if (getSelectionIDString(playerID) == RibbonWidget::NO_ITEM_ID) if (getSelectionIDString(playerID) == RibbonWidget::NO_ITEM_ID)
{ {
if (m_selection[playerID] > 0) leftPressed(playerID); if (((m_selection[playerID] > 0) && reverse ) ||
((m_selection[playerID] + 1 < int(m_active_children.size()))&& !reverse) )
{
moveToNextItem(horizontally, reverse, playerID);
}
} }
//if (m_ribbon_type != RIBBON_TOOLBAR) return result;
//{ } // moveToNextItem
//GUIEngine::transmitEvent( this, m_properties[PROP_ID], playerID );
//} // ----------------------------------------------------------------------------
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; return result;
} // leftPressed }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -549,7 +674,8 @@ EventPropagation RibbonWidget::focused(const int playerID)
if (m_active_children.size() < 1) return EVENT_LET; // empty ribbon if (m_active_children.size() < 1) return EVENT_LET; // empty ribbon
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS) if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{ {
const int mousePlayerID = input_manager->getPlayerKeyboardID(); const int mousePlayerID = input_manager->getPlayerKeyboardID();
if (m_mouse_focus == NULL && m_selection[playerID] != -1 && if (m_mouse_focus == NULL && m_selection[playerID] != -1 &&
@ -598,7 +724,8 @@ EventPropagation RibbonWidget::mouseHovered(Widget* child,
const int subbuttons_amount = m_active_children.size(); const int subbuttons_amount = m_active_children.size();
if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS) if (m_ribbon_type == RIBBON_COMBO || m_ribbon_type == RIBBON_TABS ||
m_ribbon_type == RIBBON_VERTICAL_TABS)
{ {
//Log::info("RibbonWidget", "Setting m_mouse_focus"); //Log::info("RibbonWidget", "Setting m_mouse_focus");
m_mouse_focus = child; m_mouse_focus = child;
@ -765,12 +892,8 @@ int RibbonWidget::findItemNamed(const char* internalName)
{ {
const int size = m_children.size(); const int size = m_children.size();
//printf("Ribbon : Looking for %s among %i items\n", internalName, size);
for (int n=0; n<size; n++) for (int n=0; n<size; n++)
{ {
//printf(" Ribbon : Looking for %s in item %i : %s\n",
// internalName, n, m_children[n].m_properties[PROP_ID].c_str());
if (m_children[n].m_properties[PROP_ID] == internalName) return n; if (m_children[n].m_properties[PROP_ID] == internalName) return n;
} }
return -1; return -1;

View File

@ -37,7 +37,8 @@ namespace GUIEngine
{ {
RIBBON_COMBO, //!< select one item out of many, like in a combo box RIBBON_COMBO, //!< select one item out of many, like in a combo box
RIBBON_TOOLBAR, //!< a row of individual buttons RIBBON_TOOLBAR, //!< a row of individual buttons
RIBBON_TABS //!< a tab bar RIBBON_TABS, //!< a tab bar
RIBBON_VERTICAL_TABS //!< a vertical tab bar
}; };
/** \brief A static text/icons/tabs bar widget. /** \brief A static text/icons/tabs bar widget.
@ -66,17 +67,21 @@ namespace GUIEngine
int m_selection[MAX_PLAYER_COUNT]; int m_selection[MAX_PLAYER_COUNT];
/** The type of this ribbon (toolbar, combo, tabs) */ /** The type of this ribbon (toolbar, combo, tabs, vertical tabs) */
RibbonType m_ribbon_type; RibbonType m_ribbon_type;
/** Each item within the ribbon holds a flag saying whether it is /** Each item within the ribbon holds a flag saying whether it is
* selected or not. This method updates the flag in all of this * selected or not. This method updates the flag in all of this
* ribbon's children. Called everytime selection changes.*/ * ribbon's children. Called everytime selection changes.*/
void updateSelection(); void updateSelection();
/** Callbacks */ /** Callbacks */
virtual EventPropagation rightPressed(const int playerID=0) OVERRIDE; virtual EventPropagation rightPressed(const int playerID=0) OVERRIDE;
virtual EventPropagation leftPressed(const int playerID=0) OVERRIDE; virtual EventPropagation leftPressed(const int playerID=0) OVERRIDE;
virtual EventPropagation upPressed(const int playerID=0) OVERRIDE;
virtual EventPropagation downPressed(const int playerID=0) OVERRIDE;
EventPropagation moveToNextItem(const bool horizontally, const bool reverse, const int playerID);
EventPropagation propagationType(const bool horizontally);
virtual EventPropagation mouseHovered(Widget* child, virtual EventPropagation mouseHovered(Widget* child,
const int playerID) OVERRIDE; const int playerID) OVERRIDE;
virtual EventPropagation transmitEvent(Widget* w, virtual EventPropagation transmitEvent(Widget* w,
@ -121,6 +126,10 @@ namespace GUIEngine
* for detailed descriptions) */ * for detailed descriptions) */
RibbonType getRibbonType() const { return m_ribbon_type; } RibbonType getRibbonType() const { return m_ribbon_type; }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Returns the number of active items within the ribbon */
int getActiveChildrenNumber(const int playerID) const
{ return m_active_children.size(); }
// --------------------------------------------------------------------
/** Returns the numerical ID of the selected item within the ribbon */ /** Returns the numerical ID of the selected item within the ribbon */
int getSelection(const int playerID) const int getSelection(const int playerID) const
{ return m_selection[playerID]; } { return m_selection[playerID]; }