// ---------------------------------------------------------------------------- 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("RiggonWidget", "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_space = int(roundf((float)m_w / (float)subbuttons_amount)); int widget_x = -1; // ---- add children 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_space / (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 = 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; if (message.size() == 0) widget_x += small_tab/2; else widget_x += large_tab/2; } // ---- icon ribbons else if (m_active_children[i].m_type == WTYPE_ICON_BUTTON) { if (widget_x == -1) widget_x = one_button_space/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 = file_manager->getAsset(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,"main_help.png"); image = irr_driver->getTexture(file); if(!image) Log::fatal("RibbonWidget", "Can't find fallback texture 'gui/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_space / 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_space - 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_space; } 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::add() { 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; const int subbuttons_amount = m_children.size(); // ---- 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: a little unclean to invoke layout code here? LayoutManager::readCoords(m_children.get(i), NULL, this); if (m_children[i].m_type != WTYPE_ICON_BUTTON && m_children[i].m_type != WTYPE_BUTTON) { std::cerr << "/!\\ Warning /!\\ : ribbon widgets can only have (icon)button widgets as children " << std::endl; continue; } // ribbon children must not be keyboard navigatable, the parent ribbon takes care of that if (m_children[i].m_type == WTYPE_ICON_BUTTON) { IconButtonWidget* icon = ((IconButtonWidget*)m_children.get(i)); icon->m_tab_stop = false; } total_needed_space += m_children[i].m_w; } int free_w_space = m_w - total_needed_space; //int biggest_y = 0; const int button_y = 10; float global_zoom = 1; const int min_free_space = 50; global_zoom = (float)m_w / (float)( m_w - free_w_space + min_free_space ); //free_w_space = (int)(m_w - total_needed_space*global_zoom); const int one_button_space = (int)round((float)m_w / (float)subbuttons_amount); // ---- add children for (int i=0; i<subbuttons_amount; i++) { const int widget_x = one_button_space*(i+1) - one_button_space/2; // ---- tab ribbons if (getRibbonType() == RIBBON_TABS) { IGUIButton * subbtn = NULL; rect<s32> subsize = rect<s32>(widget_x - one_button_space/2+2, 0, widget_x + one_button_space/2-2, m_h); stringw& message = m_children[i].m_text; if (m_children[i].m_type == WTYPE_BUTTON) { subbtn = GUIEngine::getGUIEnv()->addButton(subsize, btn, getNewNoFocusID(), message.c_str(), L""); subbtn->setTabStop(false); subbtn->setTabGroup(false); } else if (m_children[i].m_type == WTYPE_ICON_BUTTON) { rect<s32> icon_part = rect<s32>(15, 0, subsize.getHeight()+15, 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""); //MyGUIButton* icon = new MyGUIButton(GUIEngine::getGUIEnv(), subbtn, same_id, icon_part, true); IGUIButton* icon = GUIEngine::getGUIEnv()->addButton(icon_part, subbtn, same_id, L""); icon->setScaleImage(true); icon->setImage( irr_driver->getTexture((file_manager->getDataDir() + "/" + m_children[i].m_properties[PROP_ICON]).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); label->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); label->setTabStop(false); label->setNotClipped(true); m_labels.push_back(label); subbtn->setTabStop(false); subbtn->setTabGroup(false); } else { std::cerr << "Invalid tab bar contents\n"; } m_children[i].m_element = subbtn; } // ---- icon ribbons else if (m_children[i].m_type == WTYPE_ICON_BUTTON) { // find how much space to keep for the label under the button. // consider font size, whether the label is multiline, etc... const bool has_label = m_children[i].m_text.size() > 0; const int needed_space_under_button = has_label ? GUIEngine::getFontHeight() : 10; float imageRatio = (float)m_children[i].m_w / (float)m_children[i].m_h; // calculate the size of the image video::ITexture* image = irr_driver->getTexture((file_manager->getDataDir() + "/" + m_children[i].m_properties[PROP_ICON]).c_str()); float image_h = (float)image->getSize().Height; float image_w = image_h*imageRatio; // scale to fit (FIXME: calculate the right value directly...) float zoom = global_zoom; if (button_y + image_h*zoom + needed_space_under_button > m_h) { // scale down while (button_y + image_h*zoom + needed_space_under_button > m_h) zoom -= 0.01f; } else { // scale up while (button_y + image_h*zoom + needed_space_under_button < m_h) zoom += 0.01f; } // ---- add bitmap button part // backup and restore position in case the same object is added multiple times (FIXME: unclean) int old_x = m_children[i].m_x; int old_y = m_children[i].m_y; int old_w = m_children[i].m_w; int old_h = m_children[i].m_h; m_children[i].m_x = widget_x - (int)(image_w*zoom/2.0f); m_children[i].m_y = button_y; m_children[i].m_w = (int)(image_w*zoom); m_children[i].m_h = (int)(image_h*zoom); IconButtonWidget* icon = ((IconButtonWidget*)m_children.get(i)); icon->m_properties[PROP_EXTEND_LABEL] = StringUtils::toString(one_button_space - icon->m_w); m_children.get(i)->m_parent = btn; m_children.get(i)->add(); // restore backuped size and location (see above for more info) m_children[i].m_x = old_x; m_children[i].m_y = old_y; m_children[i].m_w = old_w; m_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 } else { std::cerr << "/!\\ Warning /!\\ : Invalid contents type in ribbon" << std::endl; } //m_children[i].id = subbtn->getID(); m_children[i].m_event_handler = this; }// next sub-button id = m_element->getID(); m_element->setTabOrder(id); m_element->setTabGroup(false); updateSelection(); }