diff --git a/data/gui/glass.stkskin b/data/gui/glass.stkskin new file mode 100644 index 000000000..762dd2ca9 --- /dev/null +++ b/data/gui/glass.stkskin @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/gui/gnu.png b/data/gui/gnu.png deleted file mode 100644 index 06befe712..000000000 Binary files a/data/gui/gnu.png and /dev/null differ diff --git a/src/gui/skin.cpp b/src/gui/skin.cpp index 07d73bd1a..f528e2191 100644 --- a/src/gui/skin.cpp +++ b/src/gui/skin.cpp @@ -9,37 +9,118 @@ using namespace GUIEngine; +/** + * Small utility to read config file info from a XML file. + */ +namespace SkinConfig + { + static std::map m_render_params; + + + static void parseElement(const XMLNode* node) + { + std::string type; + std::string state = "neutral"; + std::string image; + int leftborder = 0, rightborder=0, topborder=0, bottomborder=0; + float hborder_out_portion = 0.5f, vborder_out_portion = 1.0f; + bool preserve_h_aspect_ratios = false; + std::string areas; + + if(node->get("type", &type) == 0) + { + std::cerr << "Error in skin : All elements must have a type\n"; + return; + } + node->get("state", &state); + + if(node->get("image", &image) == 0) + { + std::cerr << "Error in skin : All elements must have an image\n"; + return; + } + + node->get("left_border", &leftborder); + node->get("right_border", &rightborder); + node->get("top_border", &topborder); + node->get("bottom_border", &bottomborder); + + node->get("hborder_out_portion", &hborder_out_portion); + node->get("vborder_out_portion", &vborder_out_portion); + + node->get("preserve_h_aspect_ratios", &preserve_h_aspect_ratios); + + node->get("areas", &areas); + + + BoxRenderParams newParam; + // TODO : look for images in a skin-specific directory + newParam.image = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/" + image).c_str() ); + newParam.left_border = leftborder; + newParam.right_border = rightborder; + newParam.top_border = topborder; + newParam.bottom_border = bottomborder; + newParam.hborder_out_portion = hborder_out_portion; + newParam.vborder_out_portion = vborder_out_portion; + newParam.preserve_h_aspect_ratios = preserve_h_aspect_ratios; + + if(areas.size() > 0) + { + newParam.areas = 0; + if(areas.find("body") != std::string::npos) newParam.areas |= BoxRenderParams::BODY; + if(areas.find("top") != std::string::npos) newParam.areas |= BoxRenderParams::TOP; + if(areas.find("bottom") != std::string::npos) newParam.areas |= BoxRenderParams::BOTTOM; + if(areas.find("left") != std::string::npos) newParam.areas |= BoxRenderParams::LEFT; + if(areas.find("right") != std::string::npos) newParam.areas |= BoxRenderParams::RIGHT; + } + + m_render_params[type+"::"+state] = newParam; + } + + static void parseColor(const XMLNode* node) + { + // TODO + } + + static void loadFromFile(std::string file) + { + XMLNode* root = file_manager->createXMLTree(file); + const int amount = root->getNumNodes(); + for(int i=0; igetNode(i); + + if(node->getName() == "element") + { + parseElement(node); + } + else if(node->getName() == "color") + { + parseColor(node); + } + else + { + std::cerr << "Unknown node in XML file : " << node->getName().c_str() << std::endl; + } + }// nend for + } + }; + + Skin::Skin(IGUISkin* fallback_skin) { + SkinConfig::loadFromFile(file_manager->getGUIDir() + "/glass.stkskin" ); + m_fallback_skin = fallback_skin; m_fallback_skin->grab(); assert(fallback_skin != NULL); - - - m_tex_button = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glassbutton.png").c_str() ); - m_tex_fbutton = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glassbutton_focused.png").c_str() ); - - m_tex_spinner = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glassspinner.png").c_str() ); - m_tex_fspinner = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glassspinner_focus.png").c_str() ); - m_tex_dspinner = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glassspinner_down.png").c_str() ); - - m_tex_tab = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasstab.png").c_str() ); - m_tex_ftab = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasstab_focus.png").c_str() ); - m_tex_dtab = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasstab_down.png").c_str() ); - + m_tex_ficonhighlight = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glass_iconhighlight_focus.png").c_str() ); - m_tex_squarefocus = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glass_square_focused.png").c_str() ); m_tex_gaugefill = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasssgauge_fill.png").c_str() ); m_tex_bubble = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/bubble.png").c_str() ); - m_tex_checkbox = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasscheckbox.png").c_str() ); - m_tex_fcheckbox = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasscheckbox_focus.png").c_str() ); - m_tex_dcheckbox = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasscheckbox_checked.png").c_str() ); - m_tex_dfcheckbox = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glasscheckbox_checked_focus.png").c_str() ); - - m_tex_section = GUIEngine::getDriver()->getTexture( (file_manager->getGUIDir() + "/glass_section.png").c_str() ); } Skin::~Skin() @@ -65,8 +146,9 @@ BoxRenderParams::BoxRenderParams() vertical_flip = false; } -void Skin::drawBoxFromStretchableTexture(const core::rect< s32 > &dest, ITexture* source, const BoxRenderParams& params) +void Skin::drawBoxFromStretchableTexture(const core::rect< s32 > &dest, const BoxRenderParams& params) { + ITexture* source = params.image; const int left_border = params.left_border; const int right_border = params.right_border; const int top_border = params.top_border; @@ -309,46 +391,14 @@ X.LowerRightCorner.Y = y1;} void Skin::drawButton(const core::rect< s32 > &rect, const bool pressed, const bool focused) { - static BoxRenderParams params; - - // FIXME - move these numbers to a config file - params.left_border = 80; - params.right_border = 80; - params.top_border = 0; - params.bottom_border = 36; - - params.preserve_h_aspect_ratios = true; - - drawBoxFromStretchableTexture(rect, (focused ? m_tex_fbutton : m_tex_button), params); + if(focused) + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["button::focused"]); + else + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["button::neutral"]); } void Skin::drawRibbon(const core::rect< s32 > &rect, const Widget* widget, const bool pressed, bool focused) { -/* - // only combo ribbons need a border - if ( ((RibbonWidget*)widget)->getRibbonType() != RIBBON_COMBO ) return; - - bool draw_border = focused; - - // check if one of its children has focus (will happen when directly clicking on them) - const int amount = widget->m_children.size(); - for(int n=0; nhasFocus(widget->m_children[n].m_element)) - { - draw_border = true; - focused = true; - break; - } - } - - if(!draw_border) return; - - if(focused) - GUIEngine::getDriver()->draw2DRectangle( SColor(255, 150, 0, 0), rect ); - else - GUIEngine::getDriver()->draw2DRectangle( SColor(255, 0, 150, 0), rect ); - */ } void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, const bool pressed, bool focused) @@ -363,30 +413,21 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, /* tab-bar ribbons */ if(type == RIBBON_TABS) { - // ribbons containing buttons are actually tabs + BoxRenderParams* params; - static BoxRenderParams params; - // FIXME - specify in file, don't hardcode - params.left_border = 75; - params.right_border = 75; - params.top_border = 0; - params.bottom_border = 15; + if(mark_selected && (focused || parent_focused)) + params = &SkinConfig::m_render_params["tab::focused"]; + else if(mark_selected) + params = &SkinConfig::m_render_params["tab::down"]; + else + params = &SkinConfig::m_render_params["tab::neutral"]; + // automatically guess from position on-screen if tabs go up or down const bool vertical_flip = rect.UpperLeftCorner.Y < GUIEngine::getDriver()->getCurrentRenderTargetSize().Height/2; - float portion_out = 0.2f; - - /* when not using plain buttons, it's probably icons, so we need more space */ - if(widget->m_type != WTYPE_BUTTON) - { - //border_below = 40; - } + params->vertical_flip = vertical_flip; core::rect< s32 > rect2 = rect; - - params.hborder_out_portion = portion_out; - params.vertical_flip = vertical_flip; - if (mark_selected) { // selected tab should be slighlty bigger than others @@ -394,23 +435,10 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, rect2.UpperLeftCorner.Y -= 10; else rect2.LowerRightCorner.Y += 10; - - drawBoxFromStretchableTexture(rect2, (focused || parent_focused ? m_tex_ftab : m_tex_dtab), params); - - /* - GUIEngine::getDriver()->draw2DLine( core::position2d< s32 >(rect2.UpperLeftCorner.X,rect2.LowerRightCorner.Y), - core::position2d< s32 >(rect2.LowerRightCorner.X,rect2.LowerRightCorner.Y), - SColor(255,255,0,0) ); - GUIEngine::getDriver()->draw2DLine( core::position2d< s32 >(rect2.UpperLeftCorner.X,rect2.UpperLeftCorner.Y), - core::position2d< s32 >(rect2.LowerRightCorner.X,rect2.UpperLeftCorner.Y), - SColor(255,255,0,0) ); - */ - - } - else - { - drawBoxFromStretchableTexture(rect2, m_tex_tab, params); } + + drawBoxFromStretchableTexture(rect2, *params); + } /* icon ribbons */ @@ -448,6 +476,7 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, core::rect source_area = core::rect(0, 0, texture_w, texture_h); + // TODO : read from skin file GUIEngine::getDriver()->draw2DImage(m_tex_bubble, rect2, source_area, 0 /* no clipping */, 0, true /* alpha */); } @@ -482,6 +511,7 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, glow_center_x + 45 + grow, glow_center_y + 25 + grow/2); + // TODO : read from skin file GUIEngine::getDriver()->draw2DImage(m_tex_ficonhighlight, rect2, source_area, 0 /* no clipping */, 0, true /* alpha */); } @@ -492,19 +522,11 @@ void Skin::drawRibbonChild(const core::rect< s32 > &rect, const Widget* widget, if(!always_show_selection && !show_focus) return; - const int texture_w = m_tex_squarefocus->getSize().Width; - const int texture_h = m_tex_squarefocus->getSize().Height; + //const int texture_w = m_tex_squarefocus->getSize().Width; + //const int texture_h = m_tex_squarefocus->getSize().Height; + //core::rect source_area = core::rect(0, 0, texture_w, texture_h); - core::rect source_area = core::rect(0, 0, texture_w, texture_h); - - static BoxRenderParams params; - params.left_border = 6; - params.right_border = 6; - params.top_border = 6; - params.bottom_border = 6; - params.hborder_out_portion = 1.0; - - drawBoxFromStretchableTexture(rect, m_tex_squarefocus, params); + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["squareSelectionHalo::neutral"]); } } // end if mark_focused @@ -530,25 +552,15 @@ void Skin::drawSpinnerBody(const core::rect< s32 > &rect, const Widget* widget, } } - static BoxRenderParams params; - // FIXME - move these numbers to a config file - params.left_border = 110; - params.right_border = 110; - params.top_border = 0; - params.bottom_border = 36; - - params.preserve_h_aspect_ratios = true; - params.hborder_out_portion = 0.0f; - - drawBoxFromStretchableTexture(rect, (focused || pressed ? m_tex_fspinner : m_tex_spinner), params); + BoxRenderParams& params = (focused || pressed) ? SkinConfig::m_render_params["spinner::focused"] : + SkinConfig::m_render_params["spinner::neutral"]; + drawBoxFromStretchableTexture(rect, params); + const SpinnerWidget* w = dynamic_cast(widget); if( w->isGauge() ) { - // the width of an handle is about 0.84 the height in the current skin. FIXME - don't hardcode. - const int handle_size = (int)( widget->h*0.84f ); - - + const int handle_size = (int)( widget->h*params.left_border/(float)params.image->getSize().Height ); const float value = (float)(w->getValue() - w->getMin()) / (w->getMax() - w->getMin()); @@ -563,6 +575,7 @@ void Skin::drawSpinnerBody(const core::rect< s32 > &rect, const Widget* widget, const core::rect< s32 > source_area = core::rect< s32 >(0, 0, texture_w, texture_h); + // TODO : make configurable through skin config file GUIEngine::getDriver()->draw2DImage(m_tex_gaugefill, dest_area, source_area, 0 /* no clipping */, 0, true /* alpha */); @@ -587,18 +600,10 @@ void Skin::drawSpinnerChild(const core::rect< s32 > &rect, Widget* widget, const spinner->x + spinner->w, spinner->y + spinner->h ); - static BoxRenderParams params; - // FIXME - move these numbers to a config file - params.left_border = 110; - params.right_border = 110; - params.top_border = 0; - params.bottom_border = 36; - - params.preserve_h_aspect_ratios = true; - params.hborder_out_portion = 0.0f; + BoxRenderParams& params = SkinConfig::m_render_params["spinner::down"]; params.areas = areas; - - drawBoxFromStretchableTexture(rect2, m_tex_dspinner, params); + drawBoxFromStretchableTexture(rect, params); + } @@ -608,72 +613,44 @@ void Skin::drawCheckBox(const core::rect< s32 > &rect, Widget* widget, bool focu { CheckBoxWidget* w = dynamic_cast(widget); - const int texture_w = m_tex_checkbox->getSize().Width; - const int texture_h = m_tex_checkbox->getSize().Height; - - const core::rect< s32 > source_area = core::rect< s32 >(0, 0, texture_w, texture_h); - - + ITexture* texture; if(w->getState() == true) { - GUIEngine::getDriver()->draw2DImage( focused ? m_tex_dfcheckbox : m_tex_dcheckbox, rect, source_area, - 0 /* no clipping */, 0, true /* alpha */); + if(focused) + texture = SkinConfig::m_render_params["checkbox::focused+checked"].image; + else + texture = SkinConfig::m_render_params["checkbox::neutral+checked"].image; } else { - GUIEngine::getDriver()->draw2DImage( focused ? m_tex_fcheckbox : m_tex_checkbox, rect, source_area, - 0 /* no clipping */, 0, true /* alpha */); + if(focused) + texture = SkinConfig::m_render_params["checkbox::focused+unchecked"].image; + else + texture = SkinConfig::m_render_params["checkbox::neutral+unchecked"].image; } + + const int texture_w = texture->getSize().Width; + const int texture_h = texture->getSize().Height; + + const core::rect< s32 > source_area = core::rect< s32 >(0, 0, texture_w, texture_h); + + GUIEngine::getDriver()->draw2DImage( texture, rect, source_area, + 0 /* no clipping */, 0, true /* alpha */); } void Skin::drawList(const core::rect< s32 > &rect, Widget* widget, bool focused) { - static BoxRenderParams params; - params.left_border = 15; - params.right_border = 15; - params.top_border = 15; - params.bottom_border = 15; - - params.hborder_out_portion = 1.0; - params.vborder_out_portion = 1.0f; - - drawBoxFromStretchableTexture( rect, m_tex_section, params ); + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["list::neutral"]); + } void Skin::drawListSelection(const core::rect< s32 > &rect, Widget* widget, bool focused) { - // focused render params - static BoxRenderParams focusparams; - focusparams.left_border = 0; - focusparams.right_border = 0; - focusparams.top_border = 0; - focusparams.bottom_border = 0; - - focusparams.hborder_out_portion = 0.0f; - focusparams.vborder_out_portion = 0.0f; - - // non-focused render params - static BoxRenderParams params; - params.left_border = 80; - params.right_border = 80; - params.top_border = 0; - params.bottom_border = 36; - - params.preserve_h_aspect_ratios = true; - params.areas = BoxRenderParams::BODY; - params.hborder_out_portion = 1.0f; - params.vborder_out_portion = 1.0f; - - // adjust render area - core::rect< s32 > rect2 = rect; - rect2.UpperLeftCorner.X -= 10; - rect2.LowerRightCorner.X += 10; - if(focused) - drawBoxFromStretchableTexture( rect2, m_tex_gaugefill, focusparams ); + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["listitem::focused"]); else - drawBoxFromStretchableTexture( rect2, m_tex_button, params ); + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["listitem::down"]); } /** recusrive function to render all sections (recursion allows to easily travesre the tree of children and sub-children) */ @@ -692,29 +669,7 @@ void Skin::renderSections(ptr_vector* within_vector) if(widget.m_show_bounding_box) { core::rect< s32 > rect = core::rect( widget.x, widget.y, widget.x + widget.w, widget.y + widget.h ); - //getDriver()->draw2DImage(g_skin->m_tex_section, rect, source_area, - // 0 /* no clipping */, 0, true /* alpha */); - - static BoxRenderParams params; - params.left_border = 15; - params.right_border = 15; - params.top_border = 15; - params.bottom_border = 15; - - params.hborder_out_portion = 1.0; - params.vborder_out_portion = 0.2f; - - drawBoxFromStretchableTexture( rect, m_tex_section, params ); - - /* - void drawBoxFromStretchableTexture(const core::rect< s32 > &dest, ITexture* source, - const int left_border, const int right_border, - const int top_border, const int bottom_border, - const bool preserve_h_aspect_ratios=false, - const float hborder_out_portion = 0.5, - int areas = BODY | LEFT | RIGHT | TOP | BOTTOM, - const bool vertical_flip=false); - */ + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["section::neutral"]); } else { @@ -831,23 +786,9 @@ void Skin::draw3DSunkenPane (IGUIElement *element, video::SColor bgcolor, bool f core::rect< s32 > Skin::draw3DWindowBackground (IGUIElement *element, bool drawTitleBar, video::SColor titleBarColor, const core::rect< s32 > &rect, const core::rect< s32 > *clip) { - // fade out background - GUIEngine::getDriver()->draw2DRectangle( SColor(150, 255, 255, 255), - core::rect< s32 >(position2d< s32 >(0,0) , GUIEngine::getDriver()->getCurrentRenderTargetSize()) ); - - static BoxRenderParams params; - params.left_border = 15; - params.right_border = 15; - params.top_border = 15; - params.bottom_border = 15; - - params.hborder_out_portion = 1.0; - params.vborder_out_portion = 0.2f; - // draw frame (since it's transluscent, draw many times to get opacity) - drawBoxFromStretchableTexture(rect, m_tex_section, params); - drawBoxFromStretchableTexture(rect, m_tex_section, params); - + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["window::neutral"]); + drawBoxFromStretchableTexture(rect, SkinConfig::m_render_params["window::neutral"]); return rect; } diff --git a/src/gui/skin.hpp b/src/gui/skin.hpp index d423cc0ce..e3d7ec459 100644 --- a/src/gui/skin.hpp +++ b/src/gui/skin.hpp @@ -20,6 +20,7 @@ namespace GUIEngine class BoxRenderParams { public: + ITexture* image; int left_border, right_border, top_border, bottom_border; bool preserve_h_aspect_ratios; float hborder_out_portion, vborder_out_portion; @@ -41,26 +42,13 @@ namespace GUIEngine { IGUISkin* m_fallback_skin; - /* f : focused; d : down state */ - ITexture* m_tex_button; - ITexture* m_tex_fbutton; - ITexture* m_tex_spinner; - ITexture* m_tex_fspinner; - ITexture* m_tex_dspinner; - ITexture* m_tex_tab; - ITexture* m_tex_ftab; - ITexture* m_tex_dtab; + ITexture* m_tex_ficonhighlight; ITexture* m_tex_bubble; - ITexture* m_tex_squarefocus; ITexture* m_tex_gaugefill; - ITexture* m_tex_checkbox; - ITexture* m_tex_fcheckbox; - ITexture* m_tex_dcheckbox; - ITexture* m_tex_dfcheckbox; - - void drawBoxFromStretchableTexture(const core::rect< s32 > &dest, ITexture* source, const BoxRenderParams& params); + + void drawBoxFromStretchableTexture(const core::rect< s32 > &dest, const BoxRenderParams& params); // my utility methods, to work around irrlicht's very Windows-95-like-look-enforcing skin system void process3DPane(IGUIElement *element, const core::rect< s32 > &rect, const bool pressed); @@ -76,8 +64,6 @@ namespace GUIEngine void drawListSelection(const core::rect< s32 > &rect, Widget* widget, bool focused); public: - ITexture* m_tex_section; - Skin(IGUISkin* fallback_skin); ~Skin();