From d1a1c5cbf055e7eacbc4aeaa53174749d4c64d87 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 15 Mar 2019 18:39:23 +0000 Subject: [PATCH] Add custom colorable GUIButton implementation --- build/android/jni/Android.mk | 1 + src/client/renderingengine.cpp | 64 +- src/gui/CMakeLists.txt | 2 + src/gui/guiButton.cpp | 649 ++++++++++++++ src/gui/guiButton.h | 306 +++++++ src/gui/guiFormSpecMenu.cpp | 4 +- src/gui/guiSkin.cpp | 1084 ++++++++++++++++++++++++ src/gui/guiSkin.h | 376 ++++++++ util/travis/clang-format-whitelist.txt | 4 + 9 files changed, 2471 insertions(+), 19 deletions(-) create mode 100644 src/gui/guiButton.cpp create mode 100644 src/gui/guiButton.h create mode 100644 src/gui/guiSkin.cpp create mode 100644 src/gui/guiSkin.h diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index f35067968..75d20ab95 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -186,6 +186,7 @@ LOCAL_SRC_FILES := \ jni/src/gui/guiPasswordChange.cpp \ jni/src/gui/guiPathSelectMenu.cpp \ jni/src/gui/guiScrollBar.cpp \ + jni/src/gui/guiSkin.cpp \ jni/src/gui/guiTable.cpp \ jni/src/gui/guiVolumeChange.cpp \ jni/src/gui/intlGUIEditBox.cpp \ diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index b18e91f6e..e56367afe 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "render/factory.h" #include "inputhandler.h" #include "gettext.h" +#include "../gui/guiSkin.h" #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__) && \ !defined(SERVER) && !defined(__HAIKU__) @@ -44,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include + #endif #ifdef __ANDROID__ @@ -52,6 +54,29 @@ with this program; if not, write to the Free Software Foundation, Inc., RenderingEngine *RenderingEngine::s_singleton = nullptr; + +static gui::GUISkin* createSkin(gui::IGUIEnvironment *environment, + gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) +{ + gui::GUISkin* skin = new gui::GUISkin(type, driver); + + gui::IGUIFont* builtinfont = environment->getBuiltInFont(); + gui::IGUIFontBitmap* bitfont = 0; + if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP) + bitfont = (gui::IGUIFontBitmap*)builtinfont; + + gui::IGUISpriteBank* bank = 0; + skin->setFont(builtinfont); + + if (bitfont) + bank = bitfont->getSpriteBank(); + + skin->setSpriteBank(bank); + + return skin; +} + + RenderingEngine::RenderingEngine(IEventReceiver *receiver) { sanity_check(!s_singleton); @@ -112,6 +137,11 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) driver = m_device->getVideoDriver(); s_singleton = this; + + auto skin = createSkin(m_device->getGUIEnvironment(), + gui::EGST_WINDOWS_METALLIC, driver); + m_device->getGUIEnvironment()->setSkin(skin); + skin->drop(); } RenderingEngine::~RenderingEngine() @@ -193,7 +223,7 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) // sort here that would call the correct toplevel setup methods for // the environment Minetest is running in but for now not deviating // from the original pattern. - + /* Setting Xorg properties for the top level window */ setupTopLevelXorgWindow(name); /* Done with Xorg properties */ @@ -211,7 +241,7 @@ bool RenderingEngine::setupTopLevelWindow(const std::string &name) /* Done with general properties */ // FIXME: setWindowIcon returns a bool result but it is unused. - // For now continue to return this result. + // For now continue to return this result. return result; } @@ -223,7 +253,7 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) Display *x11_dpl = reinterpret_cast(exposedData.OpenGLLinux.X11Display); if (x11_dpl == NULL) { warningstream << "Client: Could not find X11 Display in ExposedVideoData" - << std::endl; + << std::endl; return; } @@ -244,30 +274,30 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) // FIXME: In the future WMNormalHints should be set ... e.g see the // gtk/gdk code (gdk/x11/gdksurface-x11.c) for the setup_top_level - // method. But for now (as it would require some significant changes) - // leave the code as is. - + // method. But for now (as it would require some significant changes) + // leave the code as is. + // The following is borrowed from the above gdk source for setting top // level windows. The source indicates and the Xlib docs suggest that - // this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not - // set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is + // this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not + // set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is // required by the Extended Window Manager Hints (EWMH) spec when setting // the _NET_WM_PID (see further down) but running Minetest in an env // where the window manager is on another machine from Minetest (therefore // making the PID useless) is not expected to be a problem. Further // more, using gtk/gdk as the model it would seem that not using a FQDN is // not an issue for modern Xorg window managers. - + verbosestream << "Client: Setting Xorg window manager Properties" << std::endl; XSetWMProperties (x11_dpl, x11_win, NULL, NULL, NULL, 0, NULL, NULL, NULL); - // Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID - // (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to - // force a shutdown of an application if it doesn't respond to the destroy - // window message. - + // Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID + // (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to + // force a shutdown of an application if it doesn't respond to the destroy + // window message. + verbosestream << "Client: Setting Xorg _NET_WM_PID extened window manager property" << std::endl; @@ -277,12 +307,12 @@ void RenderingEngine::setupTopLevelXorgWindow(const std::string &name) infostream << "Client: PID is '" << static_cast(pid) << "'" << std::endl; - XChangeProperty(x11_dpl, x11_win, NET_WM_PID, - XA_CARDINAL, 32, PropModeReplace, + XChangeProperty(x11_dpl, x11_win, NET_WM_PID, + XA_CARDINAL, 32, PropModeReplace, reinterpret_cast(&pid),1); // Set the WM_CLIENT_LEADER window property here. Minetest has only one - // window and that window will always be the leader. + // window and that window will always be the leader. verbosestream << "Client: Setting Xorg WM_CLIENT_LEADER property" << std::endl; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 48ba3fa8c..2307856a4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,4 +1,5 @@ set(gui_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/guiButton.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp @@ -8,6 +9,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp new file mode 100644 index 000000000..60d330f4a --- /dev/null +++ b/src/gui/guiButton.cpp @@ -0,0 +1,649 @@ +// 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 "guiButton.h" + + +#include "IGUISkin.h" +#include "IGUIEnvironment.h" +#include "IVideoDriver.h" +#include "IGUIFont.h" +#include "porting.h" + +using namespace irr; +using namespace gui; + +//! constructor +GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent, + s32 id, core::rect rectangle, bool noclip) +: IGUIButton(environment, parent, id, rectangle), + SpriteBank(0), OverrideFont(0), + OverrideColorEnabled(false), OverrideColor(video::SColor(101,255,255,255)), + ClickTime(0), HoverTime(0), FocusTime(0), + ClickShiftState(false), ClickControlState(false), + IsPushButton(false), Pressed(false), + UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) +{ + setNotClipped(noclip); + + // This element can be tabbed. + setTabStop(true); + setTabOrder(-1); + + // PATCH + for (size_t i = 0; i < 4; i++) { + Colors[i] = Environment->getSkin()->getColor((EGUI_DEFAULT_COLOR)i); + } + // END PATCH +} + +//! destructor +GUIButton::~GUIButton() +{ + if (OverrideFont) + OverrideFont->drop(); + + if (SpriteBank) + SpriteBank->drop(); +} + + +//! Sets if the images should be scaled to fit the button +void GUIButton::setScaleImage(bool scaleImage) +{ + ScaleImage = scaleImage; +} + + +//! Returns whether the button scale the used images +bool GUIButton::isScalingImage() const +{ + return ScaleImage; +} + + +//! Sets if the button should use the skin to draw its border +void GUIButton::setDrawBorder(bool border) +{ + DrawBorder = border; +} + + +void GUIButton::setSpriteBank(IGUISpriteBank* sprites) +{ + if (sprites) + sprites->grab(); + + if (SpriteBank) + SpriteBank->drop(); + + SpriteBank = sprites; +} + +void GUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale) +{ + ButtonSprites[(u32)state].Index = index; + ButtonSprites[(u32)state].Color = color; + ButtonSprites[(u32)state].Loop = loop; + ButtonSprites[(u32)state].Scale = scale; +} + +//! Get the sprite-index for the given state or -1 when no sprite is set +s32 GUIButton::getSpriteIndex(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Index; +} + +//! Get the sprite color for the given state. Color is only used when a sprite is set. +video::SColor GUIButton::getSpriteColor(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Color; +} + +//! Returns if the sprite in the given state does loop +bool GUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Loop; +} + +//! Returns if the sprite in the given state is scaled +bool GUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const +{ + return ButtonSprites[(u32)state].Scale; +} + +//! called if an event happened. +bool GUIButton::OnEvent(const SEvent& event) +{ + if (!isEnabled()) + return IGUIElement::OnEvent(event); + + switch(event.EventType) + { + case EET_KEY_INPUT_EVENT: + if (event.KeyInput.PressedDown && + (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) + { + if (!IsPushButton) + setPressed(true); + else + setPressed(!Pressed); + + return true; + } + if (Pressed && !IsPushButton && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE) + { + setPressed(false); + return true; + } + else + if (!event.KeyInput.PressedDown && Pressed && + (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)) + { + + if (!IsPushButton) + setPressed(false); + + if (Parent) + { + ClickShiftState = event.KeyInput.Shift; + ClickControlState = event.KeyInput.Control; + + SEvent newEvent; + newEvent.EventType = EET_GUI_EVENT; + newEvent.GUIEvent.Caller = this; + newEvent.GUIEvent.Element = 0; + newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED; + Parent->OnEvent(newEvent); + } + return true; + } + break; + case EET_GUI_EVENT: + if (event.GUIEvent.Caller == this) + { + if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) + { + if (!IsPushButton) + setPressed(false); + FocusTime = (u32)porting::getTimeMs(); + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED) + { + FocusTime = (u32)porting::getTimeMs(); + } + else if (event.GUIEvent.EventType == EGET_ELEMENT_HOVERED || event.GUIEvent.EventType == EGET_ELEMENT_LEFT) + { + HoverTime = (u32)porting::getTimeMs(); + } + } + break; + case EET_MOUSE_INPUT_EVENT: + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) + { + if (!IsPushButton) + setPressed(true); + + return true; + } + else + if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) + { + bool wasPressed = Pressed; + + if ( !AbsoluteClippingRect.isPointInside( core::position2d(event.MouseInput.X, event.MouseInput.Y ) ) ) + { + if (!IsPushButton) + setPressed(false); + return true; + } + + if (!IsPushButton) + setPressed(false); + else + { + setPressed(!Pressed); + } + + if ((!IsPushButton && wasPressed && Parent) || + (IsPushButton && wasPressed != Pressed)) + { + ClickShiftState = event.MouseInput.Shift; + ClickControlState = event.MouseInput.Control; + + SEvent newEvent; + newEvent.EventType = EET_GUI_EVENT; + newEvent.GUIEvent.Caller = this; + newEvent.GUIEvent.Element = 0; + newEvent.GUIEvent.EventType = EGET_BUTTON_CLICKED; + Parent->OnEvent(newEvent); + } + + return true; + } + break; + default: + break; + } + + return Parent ? Parent->OnEvent(event) : false; +} + + +//! draws the element and its children +void GUIButton::draw() +{ + if (!IsVisible) + return; + + // PATCH + GUISkin* skin = dynamic_cast(Environment->getSkin()); + video::IVideoDriver* driver = Environment->getVideoDriver(); + // END PATCH + + if (DrawBorder) + { + if (!Pressed) + { + // PATCH + skin->drawColored3DButtonPaneStandard(this, AbsoluteRect, &AbsoluteClippingRect, Colors); + // END PATCH + } + else + { + // PATCH + skin->drawColored3DButtonPanePressed(this, AbsoluteRect, &AbsoluteClippingRect, Colors); + // END PATCH + } + } + + const core::position2di buttonCenter(AbsoluteRect.getCenter()); + EGUI_BUTTON_IMAGE_STATE imageState = getImageState(Pressed); + if ( ButtonImages[(u32)imageState].Texture ) + { + core::position2d pos(buttonCenter); + core::rect sourceRect(ButtonImages[(u32)imageState].SourceRect); + if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 ) + sourceRect = core::rect(core::position2di(0,0), ButtonImages[(u32)imageState].Texture->getOriginalSize()); + + pos.X -= sourceRect.getWidth() / 2; + pos.Y -= sourceRect.getHeight() / 2; + + if ( Pressed ) + { + // Create a pressed-down effect by moving the image when it looks identical to the unpressed state image + EGUI_BUTTON_IMAGE_STATE unpressedState = getImageState(false); + if ( unpressedState == imageState || ButtonImages[(u32)imageState] == ButtonImages[(u32)unpressedState] ) + { + pos.X += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X); + pos.Y += skin->getSize(EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y); + } + } + + driver->draw2DImage(ButtonImages[(u32)imageState].Texture, + ScaleImage? AbsoluteRect : core::rect(pos, sourceRect.getSize()), + sourceRect, &AbsoluteClippingRect, + 0, UseAlphaChannel); + } + + if (SpriteBank) + { + core::position2di pos(buttonCenter); + + if (isEnabled()) + { + // pressed / unpressed animation + EGUI_BUTTON_STATE state = Pressed ? EGBS_BUTTON_DOWN : EGBS_BUTTON_UP; + drawSprite(state, ClickTime, pos); + + // focused / unfocused animation + state = Environment->hasFocus(this) ? EGBS_BUTTON_FOCUSED : EGBS_BUTTON_NOT_FOCUSED; + drawSprite(state, FocusTime, pos); + + // mouse over / off animation + state = Environment->getHovered() == this ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF; + drawSprite(state, HoverTime, pos); + } + else + { + // draw disabled +// drawSprite(EGBS_BUTTON_DISABLED, 0, pos); + } + } + + if (Text.size()) + { + IGUIFont* font = getActiveFont(); + + core::rect rect = AbsoluteRect; + if (Pressed) + { + rect.UpperLeftCorner.X += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_X); + rect.UpperLeftCorner.Y += skin->getSize(EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y); + } + + if (font) + font->draw(Text.c_str(), rect, + OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), + true, true, &AbsoluteClippingRect); + } + + IGUIElement::draw(); +} + +void GUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center) +{ + u32 stateIdx = (u32)state; + + if (ButtonSprites[stateIdx].Index != -1) + { + if ( ButtonSprites[stateIdx].Scale ) + { + const video::SColor colors[] = {ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color}; + SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect.UpperLeftCorner, + &AbsoluteClippingRect, colors[0], // FIXME: remove [0] + porting::getTimeMs()-startTime, ButtonSprites[stateIdx].Loop); + } + else + { + SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center, + &AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, porting::getTimeMs(), + ButtonSprites[stateIdx].Loop, true); + } + } +} + +EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed) const +{ + // figure state we should have + EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED; + bool focused = Environment->hasFocus((IGUIElement*)this); + bool mouseOver = static_cast(Environment->getHovered()) == this; // (static cast for Borland) + if (isEnabled()) + { + if ( pressed ) + { + if ( focused && mouseOver ) + state = EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER; + else if ( focused ) + state = EGBIS_IMAGE_DOWN_FOCUSED; + else if ( mouseOver ) + state = EGBIS_IMAGE_DOWN_MOUSEOVER; + else + state = EGBIS_IMAGE_DOWN; + } + else // !pressed + { + if ( focused && mouseOver ) + state = EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER; + else if ( focused ) + state = EGBIS_IMAGE_UP_FOCUSED; + else if ( mouseOver ) + state = EGBIS_IMAGE_UP_MOUSEOVER; + else + state = EGBIS_IMAGE_UP; + } + } + + // find a compatible state that has images + while ( state != EGBIS_IMAGE_UP && !ButtonImages[(u32)state].Texture ) + { + switch ( state ) + { + case EGBIS_IMAGE_UP_FOCUSED: + state = EGBIS_IMAGE_UP_MOUSEOVER; + break; + case EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER: + state = EGBIS_IMAGE_UP_FOCUSED; + break; + case EGBIS_IMAGE_DOWN_MOUSEOVER: + state = EGBIS_IMAGE_DOWN; + break; + case EGBIS_IMAGE_DOWN_FOCUSED: + state = EGBIS_IMAGE_DOWN_MOUSEOVER; + break; + case EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER: + state = EGBIS_IMAGE_DOWN_FOCUSED; + break; + case EGBIS_IMAGE_DISABLED: + if ( pressed ) + state = EGBIS_IMAGE_DOWN; + else + state = EGBIS_IMAGE_UP; + break; + default: + state = EGBIS_IMAGE_UP; + } + } + + return state; +} + +//! sets another skin independent font. if this is set to zero, the button uses the font of the skin. +void GUIButton::setOverrideFont(IGUIFont* font) +{ + if (OverrideFont == font) + return; + + if (OverrideFont) + OverrideFont->drop(); + + OverrideFont = font; + + if (OverrideFont) + OverrideFont->grab(); +} + +//! Gets the override font (if any) +IGUIFont * GUIButton::getOverrideFont() const +{ + return OverrideFont; +} + +//! Get the font which is used right now for drawing +IGUIFont* GUIButton::getActiveFont() const +{ + if ( OverrideFont ) + return OverrideFont; + IGUISkin* skin = Environment->getSkin(); + if (skin) + return skin->getFont(EGDF_BUTTON); + return 0; +} + +//! Sets another color for the text. +void GUIButton::setOverrideColor(video::SColor color) +{ + OverrideColor = color; + OverrideColorEnabled = true; +} + +video::SColor GUIButton::getOverrideColor() const +{ + return OverrideColor; +} + +void GUIButton::enableOverrideColor(bool enable) +{ + OverrideColorEnabled = enable; +} + +bool GUIButton::isOverrideColorEnabled() const +{ + return OverrideColorEnabled; +} + +void GUIButton::setImage(EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image, const core::rect& sourceRect) +{ + if ( state >= EGBIS_COUNT ) + return; + + if ( image ) + image->grab(); + + u32 stateIdx = (u32)state; + if ( ButtonImages[stateIdx].Texture ) + ButtonImages[stateIdx].Texture->drop(); + + ButtonImages[stateIdx].Texture = image; + ButtonImages[stateIdx].SourceRect = sourceRect; +} + +//! Sets if the button should behave like a push button. Which means it +//! can be in two states: Normal or Pressed. With a click on the button, +//! the user can change the state of the button. +void GUIButton::setIsPushButton(bool isPushButton) +{ + IsPushButton = isPushButton; +} + + +//! Returns if the button is currently pressed +bool GUIButton::isPressed() const +{ + return Pressed; +} + + +//! Sets the pressed state of the button if this is a pushbutton +void GUIButton::setPressed(bool pressed) +{ + if (Pressed != pressed) + { + ClickTime = porting::getTimeMs(); + Pressed = pressed; + } +} + + +//! Returns whether the button is a push button +bool GUIButton::isPushButton() const +{ + return IsPushButton; +} + + +//! Sets if the alpha channel should be used for drawing images on the button (default is false) +void GUIButton::setUseAlphaChannel(bool useAlphaChannel) +{ + UseAlphaChannel = useAlphaChannel; +} + + +//! Returns if the alpha channel should be used for drawing images on the button +bool GUIButton::isAlphaChannelUsed() const +{ + return UseAlphaChannel; +} + + +bool GUIButton::isDrawingBorder() const +{ + return DrawBorder; +} + + +//! Writes attributes of the element. +void GUIButton::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const +{ + IGUIButton::serializeAttributes(out,options); + + out->addBool ("PushButton", IsPushButton ); + if (IsPushButton) + out->addBool("Pressed", Pressed); + + for ( u32 i=0; i<(u32)EGBIS_COUNT; ++i ) + { + if ( ButtonImages[i].Texture ) + { + core::stringc name( GUIButtonImageStateNames[i] ); + out->addTexture(name.c_str(), ButtonImages[i].Texture); + name += "Rect"; + out->addRect(name.c_str(), ButtonImages[i].SourceRect); + } + } + + out->addBool ("UseAlphaChannel", UseAlphaChannel); + out->addBool ("Border", DrawBorder); + out->addBool ("ScaleImage", ScaleImage); + + for ( u32 i=0; i<(u32)EGBS_COUNT; ++i ) + { + if ( ButtonSprites[i].Index >= 0 ) + { + core::stringc nameIndex( GUIButtonStateNames[i] ); + nameIndex += "Index"; + out->addInt(nameIndex.c_str(), ButtonSprites[i].Index ); + + core::stringc nameColor( GUIButtonStateNames[i] ); + nameColor += "Color"; + out->addColor(nameColor.c_str(), ButtonSprites[i].Color ); + + core::stringc nameLoop( GUIButtonStateNames[i] ); + nameLoop += "Loop"; + out->addBool(nameLoop.c_str(), ButtonSprites[i].Loop ); + + core::stringc nameScale( GUIButtonStateNames[i] ); + nameScale += "Scale"; + out->addBool(nameScale.c_str(), ButtonSprites[i].Scale ); + } + } + + // out->addString ("OverrideFont", OverrideFont); +} + + +//! Reads attributes of the element +void GUIButton::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) +{ + IGUIButton::deserializeAttributes(in,options); + + IsPushButton = in->getAttributeAsBool("PushButton"); + Pressed = IsPushButton ? in->getAttributeAsBool("Pressed") : false; + + core::rect rec = in->getAttributeAsRect("ImageRect"); + if (rec.isValid()) + setImage( in->getAttributeAsTexture("Image"), rec); + else + setImage( in->getAttributeAsTexture("Image") ); + + rec = in->getAttributeAsRect("PressedImageRect"); + if (rec.isValid()) + setPressedImage( in->getAttributeAsTexture("PressedImage"), rec); + else + setPressedImage( in->getAttributeAsTexture("PressedImage") ); + + setDrawBorder(in->getAttributeAsBool("Border")); + setUseAlphaChannel(in->getAttributeAsBool("UseAlphaChannel")); + setScaleImage(in->getAttributeAsBool("ScaleImage")); + + // setOverrideFont(in->getAttributeAsString("OverrideFont")); + + updateAbsolutePosition(); +} + +// PATCH +GUIButton* GUIButton::addButton(IGUIEnvironment *environment, const core::rect& rectangle, + IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext) +{ + GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle); + if (text) + button->setText(text); + + if ( tooltiptext ) + button->setToolTipText ( tooltiptext ); + + button->drop(); + return button; +} + +void GUIButton::setColor(video::SColor color) +{ + float d = 0.65f; + for (size_t i = 0; i < 4; i++) { + video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i); + Colors[i] = base.getInterpolated(color, d); + } +} +// END PATCH diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h new file mode 100644 index 000000000..04c24d40e --- /dev/null +++ b/src/gui/guiButton.h @@ -0,0 +1,306 @@ +// 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 + +#pragma once + +#include "IrrCompileConfig.h" + +#include "IGUIButton.h" +#include "IGUISpriteBank.h" +#include "ITexture.h" +#include "SColor.h" +#include "guiSkin.h" + +using namespace irr; + +#if (IRRLICHT_VERSION_MAJOR > 1 || IRRLICHT_VERSION_MINOR != 8 || IRRLICHT_VERSION_REVISION < 5) + namespace irr { namespace gui { + + //! State of buttons used for drawing texture images. + //! Note that only a single state is active at a time + //! Also when no image is defined for a state it will use images from another state + //! and if that state is not set from the replacement for that,etc. + //! So in many cases setting EGBIS_IMAGE_UP and EGBIS_IMAGE_DOWN is sufficient. + enum EGUI_BUTTON_IMAGE_STATE { + //! When no other states have images they will all use this one. + EGBIS_IMAGE_UP, + //! When not set EGBIS_IMAGE_UP is used. + EGBIS_IMAGE_UP_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP_MOUSEOVER is used. + EGBIS_IMAGE_UP_FOCUSED, + //! When not set EGBIS_IMAGE_UP_FOCUSED is used. + EGBIS_IMAGE_UP_FOCUSED_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP is used. + EGBIS_IMAGE_DOWN, + //! When not set EGBIS_IMAGE_DOWN is used. + EGBIS_IMAGE_DOWN_MOUSEOVER, + //! When not set EGBIS_IMAGE_DOWN_MOUSEOVER is used. + EGBIS_IMAGE_DOWN_FOCUSED, + //! When not set EGBIS_IMAGE_DOWN_FOCUSED is used. + EGBIS_IMAGE_DOWN_FOCUSED_MOUSEOVER, + //! When not set EGBIS_IMAGE_UP or EGBIS_IMAGE_DOWN are used (depending on button state). + EGBIS_IMAGE_DISABLED, + //! not used, counts the number of enumerated items + EGBIS_COUNT + }; + + //! Names for gui button image states + const c8 *const GUIButtonImageStateNames[EGBIS_COUNT + 1] = + { + "Image", // not "ImageUp" as it otherwise breaks serialization of old files + "ImageUpOver", + "ImageUpFocused", + "ImageUpFocusedOver", + "PressedImage", // not "ImageDown" as it otherwise breaks serialization of old files + "ImageDownOver", + "ImageDownFocused", + "ImageDownFocusedOver", + "ImageDisabled", + 0 // count + }; + + }} + +#endif + +class GUIButton : public gui::IGUIButton +{ +public: + + //! constructor + GUIButton(gui::IGUIEnvironment* environment, gui::IGUIElement* parent, + s32 id, core::rect rectangle, bool noclip=false); + + //! destructor + virtual ~GUIButton(); + + //! called if an event happened. + virtual bool OnEvent(const SEvent& event); + + //! draws the element and its children + virtual void draw(); + + //! sets another skin independent font. if this is set to zero, the button uses the font of the skin. + virtual void setOverrideFont(gui::IGUIFont* font=0); + + //! Gets the override font (if any) + virtual gui::IGUIFont* getOverrideFont() const; + + //! Get the font which is used right now for drawing + virtual gui::IGUIFont* getActiveFont() const; + + //! Sets another color for the button text. + virtual void setOverrideColor(video::SColor color); + + //! Gets the override color + virtual video::SColor getOverrideColor(void) const; + + //! Sets if the button text should use the override color or the color in the gui skin. + virtual void enableOverrideColor(bool enable); + + //! Checks if an override color is enabled + virtual bool isOverrideColorEnabled(void) const; + + //! Sets an image which should be displayed on the button when it is in the given state. + virtual void setImage(gui::EGUI_BUTTON_IMAGE_STATE state, video::ITexture* image=0, const core::rect& sourceRect=core::rect(0,0,0,0)); + + //! Sets an image which should be displayed on the button when it is in normal state. + virtual void setImage(video::ITexture* image=0) + { + setImage(gui::EGBIS_IMAGE_UP, image); + } + + //! Sets an image which should be displayed on the button when it is in normal state. + virtual void setImage(video::ITexture* image, const core::rect& pos) + { + setImage(gui::EGBIS_IMAGE_UP, image, pos); + } + + //! Sets an image which should be displayed on the button when it is in pressed state. + virtual void setPressedImage(video::ITexture* image=0) + { + setImage(gui::EGBIS_IMAGE_DOWN, image); + } + + //! Sets an image which should be displayed on the button when it is in pressed state. + virtual void setPressedImage(video::ITexture* image, const core::rect& pos) + { + setImage(gui::EGBIS_IMAGE_DOWN, image, pos); + } + + //! Sets the sprite bank used by the button + virtual void setSpriteBank(gui::IGUISpriteBank* bank=0); + + //! Sets the animated sprite for a specific button state + /** \param index: Number of the sprite within the sprite bank, use -1 for no sprite + \param state: State of the button to set the sprite for + \param index: The sprite number from the current sprite bank + \param color: The color of the sprite + */ + virtual void setSprite(gui::EGUI_BUTTON_STATE state, s32 index, + video::SColor color=video::SColor(255,255,255,255), + bool loop=false, bool scale=false); + + void setSprite(gui::EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) override { + setSprite(state, index, color, loop, false); + } + + //! Get the sprite-index for the given state or -1 when no sprite is set + virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const; + + //! Get the sprite color for the given state. Color is only used when a sprite is set. + virtual video::SColor getSpriteColor(gui::EGUI_BUTTON_STATE state) const; + + //! Returns if the sprite in the given state does loop + virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const; + + //! Returns if the sprite in the given state is scaled + virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const; + + //! Sets if the button should behave like a push button. Which means it + //! can be in two states: Normal or Pressed. With a click on the button, + //! the user can change the state of the button. + virtual void setIsPushButton(bool isPushButton=true); + + //! Checks whether the button is a push button + virtual bool isPushButton() const; + + //! Sets the pressed state of the button if this is a pushbutton + virtual void setPressed(bool pressed=true); + + //! Returns if the button is currently pressed + virtual bool isPressed() const; + + //! Sets if the button should use the skin to draw its border + virtual void setDrawBorder(bool border=true); + + //! Checks if the button face and border are being drawn + virtual bool isDrawingBorder() const; + + //! Sets if the alpha channel should be used for drawing images on the button (default is false) + virtual void setUseAlphaChannel(bool useAlphaChannel=true); + + //! Checks if the alpha channel should be used for drawing images on the button + virtual bool isAlphaChannelUsed() const; + + //! Sets if the button should scale the button images to fit + virtual void setScaleImage(bool scaleImage=true); + + //! Checks whether the button scales the used images + virtual bool isScalingImage() const; + + //! Get if the shift key was pressed in last EGET_BUTTON_CLICKED event + virtual bool getClickShiftState() const + { + return ClickShiftState; + } + + //! Get if the control key was pressed in last EGET_BUTTON_CLICKED event + virtual bool getClickControlState() const + { + return ClickControlState; + } + + //! Writes attributes of the element. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; + + //! Reads attributes of the element + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); + + + + void setColor(video::SColor color); + + + //! Do not drop returned handle + static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect& rectangle, + IGUIElement* parent, s32 id, const wchar_t* text, const wchar_t *tooltiptext=L""); + +protected: + void drawSprite(gui::EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center); + gui::EGUI_BUTTON_IMAGE_STATE getImageState(bool pressed) const; + +private: + + struct ButtonSprite + { + ButtonSprite() : Index(-1), Loop(false), Scale(false) + { + } + + bool operator==(const ButtonSprite& other) const + { + return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale; + } + + s32 Index; + video::SColor Color; + bool Loop; + bool Scale; + }; + + ButtonSprite ButtonSprites[gui::EGBS_COUNT]; + gui::IGUISpriteBank* SpriteBank; + + struct ButtonImage + { + ButtonImage() : Texture(0), SourceRect(core::rect(0,0,0,0)) + { + } + + ButtonImage(const ButtonImage& other) : Texture(0), SourceRect(core::rect(0,0,0,0)) + { + *this = other; + } + + ~ButtonImage() + { + if ( Texture ) + Texture->drop(); + } + + ButtonImage& operator=(const ButtonImage& other) + { + if ( this == &other ) + return *this; + + if (other.Texture) + other.Texture->grab(); + if ( Texture ) + Texture->drop(); + Texture = other.Texture; + SourceRect = other.SourceRect; + return *this; + } + + bool operator==(const ButtonImage& other) const + { + return Texture == other.Texture && SourceRect == other.SourceRect; + } + + + video::ITexture* Texture; + core::rect SourceRect; + }; + + ButtonImage ButtonImages[gui::EGBIS_COUNT]; + + gui::IGUIFont* OverrideFont; + + bool OverrideColorEnabled; + video::SColor OverrideColor; + + u32 ClickTime, HoverTime, FocusTime; + + bool ClickShiftState; + bool ClickControlState; + + bool IsPushButton; + bool Pressed; + bool UseAlphaChannel; + bool DrawBorder; + bool ScaleImage; + + video::SColor Colors[4]; +}; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 4d3409f3b..382fbbd54 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "guiButton.h" #include "guiFormSpecMenu.h" #include "guiTable.h" #include "constants.h" @@ -698,9 +699,8 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, spec.ftype = f_Button; if(type == "button_exit") spec.is_exit = true; - gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid, - spec.flabel.c_str()); + GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp new file mode 100644 index 000000000..8892a00b4 --- /dev/null +++ b/src/gui/guiSkin.cpp @@ -0,0 +1,1084 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2019 Irrlick +// +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "guiSkin.h" +#ifdef _IRR_COMPILE_WITH_GUI_ + +#include "IGUIFont.h" +#include "IGUISpriteBank.h" +#include "IGUIElement.h" +#include "IVideoDriver.h" +#include "IAttributes.h" + +namespace irr +{ +namespace gui +{ + +GUISkin::GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) +: SpriteBank(0), Driver(driver), Type(type) +{ + #ifdef _DEBUG + setDebugName("GUISkin"); + #endif + + if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) + { + Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50); + Colors[EGDC_3D_SHADOW] = video::SColor(101,130,130,130); + Colors[EGDC_3D_FACE] = video::SColor(101,210,210,210); + Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101,255,255,255); + Colors[EGDC_3D_LIGHT] = video::SColor(101,210,210,210); + Colors[EGDC_ACTIVE_BORDER] = video::SColor(101,16,14,115); + Colors[EGDC_ACTIVE_CAPTION] = video::SColor(255,255,255,255); + Colors[EGDC_APP_WORKSPACE] = video::SColor(101,100,100,100); + Colors[EGDC_BUTTON_TEXT] = video::SColor(240,10,10,10); + Colors[EGDC_GRAY_TEXT] = video::SColor(240,130,130,130); + Colors[EGDC_HIGH_LIGHT] = video::SColor(101,8,36,107); + Colors[EGDC_HIGH_LIGHT_TEXT] = video::SColor(240,255,255,255); + Colors[EGDC_INACTIVE_BORDER] = video::SColor(101,165,165,165); + Colors[EGDC_INACTIVE_CAPTION] = video::SColor(255,30,30,30); + Colors[EGDC_TOOLTIP] = video::SColor(200,0,0,0); + Colors[EGDC_TOOLTIP_BACKGROUND] = video::SColor(200,255,255,225); + Colors[EGDC_SCROLLBAR] = video::SColor(101,230,230,230); + Colors[EGDC_WINDOW] = video::SColor(101,255,255,255); + Colors[EGDC_WINDOW_SYMBOL] = video::SColor(200,10,10,10); + Colors[EGDC_ICON] = video::SColor(200,255,255,255); + Colors[EGDC_ICON_HIGH_LIGHT] = video::SColor(200,8,36,107); + Colors[EGDC_GRAY_WINDOW_SYMBOL] = video::SColor(240,100,100,100); + Colors[EGDC_EDITABLE] = video::SColor(255,255,255,255); + Colors[EGDC_GRAY_EDITABLE] = video::SColor(255,120,120,120); + Colors[EGDC_FOCUSED_EDITABLE] = video::SColor(255,240,240,255); + + + Sizes[EGDS_SCROLLBAR_SIZE] = 14; + Sizes[EGDS_MENU_HEIGHT] = 30; + Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15; + Sizes[EGDS_CHECK_BOX_WIDTH] = 18; + Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500; + Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200; + Sizes[EGDS_BUTTON_WIDTH] = 80; + Sizes[EGDS_BUTTON_HEIGHT] = 30; + + Sizes[EGDS_TEXT_DISTANCE_X] = 2; + Sizes[EGDS_TEXT_DISTANCE_Y] = 0; + + Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 2; + Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 0; + } + else + { + //0x80a6a8af + Colors[EGDC_3D_DARK_SHADOW] = 0x60767982; + //Colors[EGDC_3D_FACE] = 0xc0c9ccd4; // tab background + Colors[EGDC_3D_FACE] = 0xc0cbd2d9; // tab background + Colors[EGDC_3D_SHADOW] = 0x50e4e8f1; // tab background, and left-top highlight + Colors[EGDC_3D_HIGH_LIGHT] = 0x40c7ccdc; + Colors[EGDC_3D_LIGHT] = 0x802e313a; + Colors[EGDC_ACTIVE_BORDER] = 0x80404040; // window title + Colors[EGDC_ACTIVE_CAPTION] = 0xffd0d0d0; + Colors[EGDC_APP_WORKSPACE] = 0xc0646464; // unused + Colors[EGDC_BUTTON_TEXT] = 0xd0161616; + Colors[EGDC_GRAY_TEXT] = 0x3c141414; + Colors[EGDC_HIGH_LIGHT] = 0x6c606060; + Colors[EGDC_HIGH_LIGHT_TEXT] = 0xd0e0e0e0; + Colors[EGDC_INACTIVE_BORDER] = 0xf0a5a5a5; + Colors[EGDC_INACTIVE_CAPTION] = 0xffd2d2d2; + Colors[EGDC_TOOLTIP] = 0xf00f2033; + Colors[EGDC_TOOLTIP_BACKGROUND] = 0xc0cbd2d9; + Colors[EGDC_SCROLLBAR] = 0xf0e0e0e0; + Colors[EGDC_WINDOW] = 0xf0f0f0f0; + Colors[EGDC_WINDOW_SYMBOL] = 0xd0161616; + Colors[EGDC_ICON] = 0xd0161616; + Colors[EGDC_ICON_HIGH_LIGHT] = 0xd0606060; + Colors[EGDC_GRAY_WINDOW_SYMBOL] = 0x3c101010; + Colors[EGDC_EDITABLE] = 0xf0ffffff; + Colors[EGDC_GRAY_EDITABLE] = 0xf0cccccc; + Colors[EGDC_FOCUSED_EDITABLE] = 0xf0fffff0; + + Sizes[EGDS_SCROLLBAR_SIZE] = 14; + Sizes[EGDS_MENU_HEIGHT] = 48; + Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15; + Sizes[EGDS_CHECK_BOX_WIDTH] = 18; + Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500; + Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200; + Sizes[EGDS_BUTTON_WIDTH] = 80; + Sizes[EGDS_BUTTON_HEIGHT] = 30; + + Sizes[EGDS_TEXT_DISTANCE_X] = 3; + Sizes[EGDS_TEXT_DISTANCE_Y] = 2; + + Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 3; + Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 2; + } + + Sizes[EGDS_MESSAGE_BOX_GAP_SPACE] = 15; + Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH] = 0; + Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_WIDTH] = 500; + Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT] = 0; + Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT] = 99999; + + Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X] = 1; + Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y] = 1; + Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0; + Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2; + + Texts[EGDT_MSG_BOX_OK] = L"OK"; + Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; + Texts[EGDT_MSG_BOX_YES] = L"Yes"; + Texts[EGDT_MSG_BOX_NO] = L"No"; + Texts[EGDT_WINDOW_CLOSE] = L"Close"; + Texts[EGDT_WINDOW_RESTORE] = L"Restore"; + Texts[EGDT_WINDOW_MINIMIZE] = L"Minimize"; + Texts[EGDT_WINDOW_MAXIMIZE] = L"Maximize"; + + Icons[EGDI_WINDOW_MAXIMIZE] = 225; + Icons[EGDI_WINDOW_RESTORE] = 226; + Icons[EGDI_WINDOW_CLOSE] = 227; + Icons[EGDI_WINDOW_MINIMIZE] = 228; + Icons[EGDI_CURSOR_UP] = 229; + Icons[EGDI_CURSOR_DOWN] = 230; + Icons[EGDI_CURSOR_LEFT] = 231; + Icons[EGDI_CURSOR_RIGHT] = 232; + Icons[EGDI_MENU_MORE] = 232; + Icons[EGDI_CHECK_BOX_CHECKED] = 233; + Icons[EGDI_DROP_DOWN] = 234; + Icons[EGDI_SMALL_CURSOR_UP] = 235; + Icons[EGDI_SMALL_CURSOR_DOWN] = 236; + Icons[EGDI_RADIO_BUTTON_CHECKED] = 237; + Icons[EGDI_MORE_LEFT] = 238; + Icons[EGDI_MORE_RIGHT] = 239; + Icons[EGDI_MORE_UP] = 240; + Icons[EGDI_MORE_DOWN] = 241; + Icons[EGDI_WINDOW_RESIZE] = 242; + Icons[EGDI_EXPAND] = 243; + Icons[EGDI_COLLAPSE] = 244; + + Icons[EGDI_FILE] = 245; + Icons[EGDI_DIRECTORY] = 246; + + for (u32 i=0; idrop(); + } + + if (SpriteBank) + SpriteBank->drop(); +} + + +//! returns default color +video::SColor GUISkin::getColor(EGUI_DEFAULT_COLOR color) const +{ + if ((u32)color < EGDC_COUNT) + return Colors[color]; + else + return video::SColor(); +} + + +//! sets a default color +void GUISkin::setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) +{ + if ((u32)which < EGDC_COUNT) + Colors[which] = newColor; +} + + +//! returns size for the given size type +s32 GUISkin::getSize(EGUI_DEFAULT_SIZE size) const +{ + if ((u32)size < EGDS_COUNT) + return Sizes[size]; + else + return 0; +} + + +//! sets a default size +void GUISkin::setSize(EGUI_DEFAULT_SIZE which, s32 size) +{ + if ((u32)which < EGDS_COUNT) + Sizes[which] = size; +} + + +//! returns the default font +IGUIFont* GUISkin::getFont(EGUI_DEFAULT_FONT which) const +{ + if (((u32)which < EGDF_COUNT) && Fonts[which]) + return Fonts[which]; + else + return Fonts[EGDF_DEFAULT]; +} + + +//! sets a default font +void GUISkin::setFont(IGUIFont* font, EGUI_DEFAULT_FONT which) +{ + if ((u32)which >= EGDF_COUNT) + return; + + if (font) + { + font->grab(); + if (Fonts[which]) + Fonts[which]->drop(); + + Fonts[which] = font; + } +} + + +//! gets the sprite bank stored +IGUISpriteBank* GUISkin::getSpriteBank() const +{ + return SpriteBank; +} + + +//! set a new sprite bank or remove one by passing 0 +void GUISkin::setSpriteBank(IGUISpriteBank* bank) +{ + if (bank) + bank->grab(); + + if (SpriteBank) + SpriteBank->drop(); + + SpriteBank = bank; +} + + +//! Returns a default icon +u32 GUISkin::getIcon(EGUI_DEFAULT_ICON icon) const +{ + if ((u32)icon < EGDI_COUNT) + return Icons[icon]; + else + return 0; +} + + +//! Sets a default icon +void GUISkin::setIcon(EGUI_DEFAULT_ICON icon, u32 index) +{ + if ((u32)icon < EGDI_COUNT) + Icons[icon] = index; +} + + +//! Returns a default text. For example for Message box button captions: +//! "OK", "Cancel", "Yes", "No" and so on. +const wchar_t* GUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const +{ + if ((u32)text < EGDT_COUNT) + return Texts[text].c_str(); + else + return Texts[0].c_str(); +} + + +//! Sets a default text. For example for Message box button captions: +//! "OK", "Cancel", "Yes", "No" and so on. +void GUISkin::setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText) +{ + if ((u32)which < EGDT_COUNT) + Texts[which] = newText; +} + + +//! draws a standard 3d button pane +/** Used for drawing for example buttons in normal state. +It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and +EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. +\param rect: Defining area where to draw. +\param clip: Clip area. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. */ +// PATCH +void GUISkin::drawColored3DButtonPaneStandard(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect rect = r; + + if ( Type == EGST_BURNING_SKIN ) + { + rect.UpperLeftCorner.X -= 1; + rect.UpperLeftCorner.Y -= 1; + rect.LowerRightCorner.X += 1; + rect.LowerRightCorner.Y += 1; + draw3DSunkenPane(element, + colors[ EGDC_WINDOW ].getInterpolated( 0xFFFFFFFF, 0.9f ) + ,false, true, rect, clip); + return; + } + + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect.LowerRightCorner.X -= 1; + rect.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + + rect.UpperLeftCorner.X += 1; + rect.UpperLeftCorner.Y += 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + + rect.LowerRightCorner.X -= 1; + rect.LowerRightCorner.Y -= 1; + + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); + Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); + } +} +// END PATCH + + +//! draws a pressed 3d button pane +/** Used for drawing for example buttons in pressed state. +It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and +EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. +\param rect: Defining area where to draw. +\param clip: Clip area. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. */ +// PATCH +void GUISkin::drawColored3DButtonPanePressed(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect rect = r; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + + rect.LowerRightCorner.X -= 1; + rect.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect.UpperLeftCorner.X += 1; + rect.UpperLeftCorner.Y += 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + + rect.UpperLeftCorner.X += 1; + rect.UpperLeftCorner.Y += 1; + + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); + Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); + } +} +// END PATCH + + +//! draws a sunken 3d pane +/** Used for drawing the background of edit, combo or check boxes. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. +\param bgcolor: Background color. +\param flat: Specifies if the sunken pane should be flat or displayed as sunken +deep into the ground. +\param rect: Defining area where to draw. +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColored3DSunkenPane(IGUIElement* element, video::SColor bgcolor, + bool flat, bool fillBackGround, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect rect = r; + + if (fillBackGround) + Driver->draw2DRectangle(bgcolor, rect, clip); + + if (flat) + { + // draw flat sunken pane + + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top + + ++rect.UpperLeftCorner.Y; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left + + rect = r; + ++rect.UpperLeftCorner.Y; + rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right + + rect = r; + ++rect.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; + --rect.LowerRightCorner.X; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom + } + else + { + // draw deep sunken pane + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top + ++rect.UpperLeftCorner.X; + ++rect.UpperLeftCorner.Y; + --rect.LowerRightCorner.X; + ++rect.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect.UpperLeftCorner.X = r.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y+1; + rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left + ++rect.UpperLeftCorner.X; + ++rect.UpperLeftCorner.Y; + ++rect.LowerRightCorner.X; + --rect.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect = r; + rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; + ++rect.UpperLeftCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right + --rect.UpperLeftCorner.X; + ++rect.UpperLeftCorner.Y; + --rect.LowerRightCorner.X; + --rect.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); + + rect = r; + ++rect.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; + --rect.LowerRightCorner.X; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom + ++rect.UpperLeftCorner.X; + --rect.UpperLeftCorner.Y; + --rect.LowerRightCorner.X; + --rect.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); + } +} +// END PATCH + +//! draws a window background +// return where to draw title bar text. +// PATCH +core::rect GUISkin::drawColored3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& r, + const core::rect* clip, + core::rect* checkClientArea, + const video::SColor* colors) +{ + if (!Driver) + { + if ( checkClientArea ) + { + *checkClientArea = r; + } + return r; + } + + if (!colors) + colors = Colors; + + core::rect rect = r; + + // top border + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + } + + // left border + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + } + + // right border dark outer line + rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1; + rect.LowerRightCorner.X = r.LowerRightCorner.X; + rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + } + + // right border bright innner line + rect.UpperLeftCorner.X -= 1; + rect.LowerRightCorner.X -= 1; + rect.UpperLeftCorner.Y += 1; + rect.LowerRightCorner.Y -= 1; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + } + + // bottom border dark outer line + rect.UpperLeftCorner.X = r.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = r.LowerRightCorner.X; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + } + + // bottom border bright inner line + rect.UpperLeftCorner.X += 1; + rect.LowerRightCorner.X -= 1; + rect.UpperLeftCorner.Y -= 1; + rect.LowerRightCorner.Y -= 1; + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + } + + // client area for background + rect = r; + rect.UpperLeftCorner.X +=1; + rect.UpperLeftCorner.Y +=1; + rect.LowerRightCorner.X -= 2; + rect.LowerRightCorner.Y -= 2; + if (checkClientArea) + { + *checkClientArea = rect; + } + + if ( !checkClientArea ) + { + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c1 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.9f ); + const video::SColor c2 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.8f ); + + Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); + } + else + { + const video::SColor c2 = colors[EGDC_3D_SHADOW]; + const video::SColor c1 = colors[EGDC_3D_FACE]; + Driver->draw2DRectangle(rect, c1, c1, c1, c2, clip); + } + } + + // title bar + rect = r; + rect.UpperLeftCorner.X += 2; + rect.UpperLeftCorner.Y += 2; + rect.LowerRightCorner.X -= 2; + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + getSize(EGDS_WINDOW_BUTTON_WIDTH) + 2; + + if (drawTitleBar ) + { + if (checkClientArea) + { + (*checkClientArea).UpperLeftCorner.Y = rect.LowerRightCorner.Y; + } + else + { + // draw title bar + //if (!UseGradient) + // Driver->draw2DRectangle(titleBarColor, rect, clip); + //else + if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c = titleBarColor.getInterpolated( video::SColor(titleBarColor.getAlpha(),255,255,255), 0.8f); + Driver->draw2DRectangle(rect, titleBarColor, titleBarColor, c, c, clip); + } + else + { + const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(),0,0,0), 0.2f); + Driver->draw2DRectangle(rect, titleBarColor, c, titleBarColor, c, clip); + } + } + } + + return rect; +} +// END PATCH + + +//! draws a standard 3d menu pane +/** Used for drawing for menus and context menus. +It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and +EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. +\param rect: Defining area where to draw. +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColored3DMenuPane(IGUIElement* element, + const core::rect& r, const core::rect* clip, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect rect = r; + + if ( Type == EGST_BURNING_SKIN ) + { + rect.UpperLeftCorner.Y -= 3; + draw3DButtonPaneStandard(element, rect, clip); + return; + } + + // in this skin, this is exactly what non pressed buttons look like, + // so we could simply call + // draw3DButtonPaneStandard(element, rect, clip); + // here. + // but if the skin is transparent, this doesn't look that nice. So + // We draw it a little bit better, with some more draw2DRectangle calls, + // but there aren't that much menus visible anyway. + + rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); + + rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1; + rect.LowerRightCorner.X = r.LowerRightCorner.X; + rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect.UpperLeftCorner.X -= 1; + rect.LowerRightCorner.X -= 1; + rect.UpperLeftCorner.Y += 1; + rect.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + + rect.UpperLeftCorner.X = r.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = r.LowerRightCorner.X; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); + + rect.UpperLeftCorner.X += 1; + rect.LowerRightCorner.X -= 1; + rect.UpperLeftCorner.Y -= 1; + rect.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + + rect = r; + rect.UpperLeftCorner.X +=1; + rect.UpperLeftCorner.Y +=1; + rect.LowerRightCorner.X -= 2; + rect.LowerRightCorner.Y -= 2; + + if (!UseGradient) + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = colors[EGDC_3D_SHADOW]; + Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); + } +} +// END PATCH + + +//! draws a standard 3d tool bar +/** Used for drawing for toolbars and menus. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. +\param rect: Defining area where to draw. +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColored3DToolBar(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect rect = r; + + rect.UpperLeftCorner.X = r.UpperLeftCorner.X; + rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; + rect.LowerRightCorner.Y = r.LowerRightCorner.Y; + rect.LowerRightCorner.X = r.LowerRightCorner.X; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); + + rect = r; + rect.LowerRightCorner.Y -= 1; + + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c1 = 0xF0000000 | colors[EGDC_3D_FACE].color; + const video::SColor c2 = 0xF0000000 | colors[EGDC_3D_SHADOW].color; + + rect.LowerRightCorner.Y += 1; + Driver->draw2DRectangle(rect, c1, c2, c1, c2, clip); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = colors[EGDC_3D_SHADOW]; + Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); + } +} +// END PATCH + +//! draws a tab button +/** Used for drawing for tab buttons on top of tabs. +\param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. +\param active: Specifies if the tab is currently active. +\param rect: Defining area where to draw. +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColored3DTabButton(IGUIElement* element, bool active, + const core::rect& frameRect, const core::rect* clip, EGUI_ALIGNMENT alignment, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect tr = frameRect; + + if ( alignment == EGUIA_UPPERLEFT ) + { + tr.LowerRightCorner.X -= 2; + tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; + tr.UpperLeftCorner.X += 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw left highlight + tr = frameRect; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + tr.UpperLeftCorner.Y += 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw grey background + tr = frameRect; + tr.UpperLeftCorner.X += 1; + tr.UpperLeftCorner.Y += 1; + tr.LowerRightCorner.X -= 2; + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); + + // draw right middle gray shadow + tr.LowerRightCorner.X += 1; + tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + + tr.LowerRightCorner.X += 1; + tr.UpperLeftCorner.X += 1; + tr.UpperLeftCorner.Y += 1; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); + } + else + { + tr.LowerRightCorner.X -= 2; + tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; + tr.UpperLeftCorner.X += 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw left highlight + tr = frameRect; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + tr.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw grey background + tr = frameRect; + tr.UpperLeftCorner.X += 1; + tr.UpperLeftCorner.Y -= 1; + tr.LowerRightCorner.X -= 2; + tr.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); + + // draw right middle gray shadow + tr.LowerRightCorner.X += 1; + tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; + //tr.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + + tr.LowerRightCorner.X += 1; + tr.UpperLeftCorner.X += 1; + tr.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); + } +} +// END PATCH + + +//! draws a tab control body +/** \param element: Pointer to the element which wishes to draw this. This parameter +is usually not used by ISkin, but can be used for example by more complex +implementations to find out how to draw the part exactly. +\param border: Specifies if the border should be drawn. +\param background: Specifies if the background should be drawn. +\param rect: Defining area where to draw. +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColored3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip, s32 tabHeight, EGUI_ALIGNMENT alignment, + const video::SColor* colors) +{ + if (!Driver) + return; + + if (!colors) + colors = Colors; + + core::rect tr = rect; + + if ( tabHeight == -1 ) + tabHeight = getSize(gui::EGDS_BUTTON_HEIGHT); + + // draw border. + if (border) + { + if ( alignment == EGUIA_UPPERLEFT ) + { + // draw left hightlight + tr.UpperLeftCorner.Y += tabHeight + 2; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw right shadow + tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + + // draw lower shadow + tr = rect; + tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + } + else + { + // draw left hightlight + tr.LowerRightCorner.Y -= tabHeight + 2; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + + // draw right shadow + tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; + tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + + // draw lower shadow + tr = rect; + tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); + } + } + + if (background) + { + if ( alignment == EGUIA_UPPERLEFT ) + { + tr = rect; + tr.UpperLeftCorner.Y += tabHeight + 2; + tr.LowerRightCorner.X -= 1; + tr.UpperLeftCorner.X += 1; + tr.LowerRightCorner.Y -= 1; + } + else + { + tr = rect; + tr.UpperLeftCorner.X += 1; + tr.UpperLeftCorner.Y -= 1; + tr.LowerRightCorner.X -= 1; + tr.LowerRightCorner.Y -= tabHeight + 2; + //tr.UpperLeftCorner.X += 1; + } + + if (!UseGradient) + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); + else + { + video::SColor c1 = colors[EGDC_3D_FACE]; + video::SColor c2 = colors[EGDC_3D_SHADOW]; + Driver->draw2DRectangle(tr, c1, c1, c2, c2, clip); + } + } +} +// END PATCH + + +//! draws an icon, usually from the skin's sprite bank +/** \param parent: Pointer to the element which wishes to draw this icon. +This parameter is usually not used by IGUISkin, but can be used for example +by more complex implementations to find out how to draw the part exactly. +\param icon: Specifies the icon to be drawn. +\param position: The position to draw the icon +\param starttime: The time at the start of the animation +\param currenttime: The present time, used to calculate the frame number +\param loop: Whether the animation should loop or not +\param clip: Clip area. */ +// PATCH +void GUISkin::drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime, u32 currenttime, + bool loop, const core::rect* clip, + const video::SColor* colors) +{ + if (!SpriteBank) + return; + + if (!colors) + colors = Colors; + + bool gray = element && !element->isEnabled(); + SpriteBank->draw2DSprite(Icons[icon], position, clip, + colors[gray? EGDC_GRAY_WINDOW_SYMBOL : EGDC_WINDOW_SYMBOL], starttime, currenttime, loop, true); +} +// END PATCH + + +EGUI_SKIN_TYPE GUISkin::getType() const +{ + return Type; +} + + +//! draws a 2d rectangle. +void GUISkin::draw2DRectangle(IGUIElement* element, + const video::SColor &color, const core::rect& pos, + const core::rect* clip) +{ + Driver->draw2DRectangle(color, pos, clip); +} + + +//! Writes attributes of the object. +//! Implement this to expose the attributes of your scene node animator for +//! scripting languages, editors, debuggers or xml serialization purposes. +void GUISkin::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const +{ + u32 i; + for (i=0; iaddColor(GUISkinColorNames[i], Colors[i]); + + for (i=0; iaddInt(GUISkinSizeNames[i], Sizes[i]); + + for (i=0; iaddString(GUISkinTextNames[i], Texts[i].c_str()); + + for (i=0; iaddInt(GUISkinIconNames[i], Icons[i]); +} + + +//! Reads attributes of the object. +//! Implement this to set the attributes of your scene node animator for +//! scripting languages, editors, debuggers or xml deserialization purposes. +void GUISkin::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) +{ + // TODO: This is not nice code for downward compatibility, whenever new values are added and users + // load an old skin the corresponding values will be set to 0. + u32 i; + for (i=0; igetAttributeAsColor(GUISkinColorNames[i]); + + for (i=0; igetAttributeAsInt(GUISkinSizeNames[i]); + + for (i=0; igetAttributeAsStringW(GUISkinTextNames[i]); + + for (i=0; igetAttributeAsInt(GUISkinIconNames[i]); +} + + +//! gets the colors +// PATCH +void GUISkin::getColors(video::SColor* colors) +{ + u32 i; + for (i=0; i +#include "ITexture.h" + +namespace irr +{ +namespace video +{ + class IVideoDriver; +} +namespace gui +{ + class GUISkin : public IGUISkin + { + public: + + GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver); + + //! destructor + virtual ~GUISkin(); + + //! returns default color + virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const; + + //! sets a default color + virtual void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor); + + //! returns size for the given size type + virtual s32 getSize(EGUI_DEFAULT_SIZE size) const; + + //! sets a default size + virtual void setSize(EGUI_DEFAULT_SIZE which, s32 size); + + //! returns the default font + virtual IGUIFont* getFont(EGUI_DEFAULT_FONT which=EGDF_DEFAULT) const; + + //! sets a default font + virtual void setFont(IGUIFont* font, EGUI_DEFAULT_FONT which=EGDF_DEFAULT); + + //! sets the sprite bank used for drawing icons + virtual void setSpriteBank(IGUISpriteBank* bank); + + //! gets the sprite bank used for drawing icons + virtual IGUISpriteBank* getSpriteBank() const; + + //! Returns a default icon + /** Returns the sprite index within the sprite bank */ + virtual u32 getIcon(EGUI_DEFAULT_ICON icon) const; + + //! Sets a default icon + /** Sets the sprite index used for drawing icons like arrows, + close buttons and ticks in checkboxes + \param icon: Enum specifying which icon to change + \param index: The sprite index used to draw this icon */ + virtual void setIcon(EGUI_DEFAULT_ICON icon, u32 index); + + //! Returns a default text. + /** For example for Message box button captions: + "OK", "Cancel", "Yes", "No" and so on. */ + virtual const wchar_t* getDefaultText(EGUI_DEFAULT_TEXT text) const; + + //! Sets a default text. + /** For example for Message box button captions: + "OK", "Cancel", "Yes", "No" and so on. */ + virtual void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText); + + //! draws a standard 3d button pane + /** Used for drawing for example buttons in normal state. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. */ + virtual void draw3DButtonPaneStandard(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DButtonPaneStandard(element, rect,clip); + } + + virtual void drawColored3DButtonPaneStandard(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a pressed 3d button pane + /** Used for drawing for example buttons in pressed state. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. */ + virtual void draw3DButtonPanePressed(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DButtonPanePressed(element, rect, clip); + } + + virtual void drawColored3DButtonPanePressed(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a sunken 3d pane + /** Used for drawing the background of edit, combo or check boxes. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param bgcolor: Background color. + \param flat: Specifies if the sunken pane should be flat or displayed as sunken + deep into the ground. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DSunkenPane(IGUIElement* element, + video::SColor bgcolor, bool flat, + bool fillBackGround, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DSunkenPane(element, bgcolor, flat, fillBackGround, rect, clip); + } + + virtual void drawColored3DSunkenPane(IGUIElement* element, + video::SColor bgcolor, bool flat, + bool fillBackGround, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a window background + /** Used for drawing the background of dialogs and windows. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param titleBarColor: Title color. + \param drawTitleBar: True to enable title drawing. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param checkClientArea: When set to non-null the function will not draw anything, + but will instead return the clientArea which can be used for drawing by the calling window. + That is the area without borders and without titlebar. + \return Returns rect where it would be good to draw title bar text. This will + work even when checkClientArea is set to a non-null value.*/ + virtual core::rect draw3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& rect, + const core::rect* clip, + core::rect* checkClientArea) + { + return drawColored3DWindowBackground(element, drawTitleBar, titleBarColor, + rect, clip, checkClientArea); + } + + virtual core::rect drawColored3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& rect, + const core::rect* clip, + core::rect* checkClientArea, + const video::SColor* colors=0); + + //! draws a standard 3d menu pane + /** Used for drawing for menus and context menus. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DMenuPane(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DMenuPane(element, rect, clip); + } + + virtual void drawColored3DMenuPane(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a standard 3d tool bar + /** Used for drawing for toolbars and menus. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DToolBar(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DToolBar(element, rect, clip); + } + + virtual void drawColored3DToolBar(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a tab button + /** Used for drawing for tab buttons on top of tabs. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param active: Specifies if the tab is currently active. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DTabButton(IGUIElement* element, bool active, + const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) + { + drawColored3DTabButton(element, active, rect, clip, alignment); + } + + virtual void drawColored3DTabButton(IGUIElement* element, bool active, + const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, + const video::SColor* colors=0); + + //! draws a tab control body + /** \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param border: Specifies if the border should be drawn. + \param background: Specifies if the background should be drawn. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) + { + drawColored3DTabBody(element, border, background, rect, clip, tabHeight, alignment); + } + + virtual void drawColored3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, + const video::SColor* colors=0); + + //! draws an icon, usually from the skin's sprite bank + /** \param element: Pointer to the element which wishes to draw this icon. + This parameter is usually not used by IGUISkin, but can be used for example + by more complex implementations to find out how to draw the part exactly. + \param icon: Specifies the icon to be drawn. + \param position: The position to draw the icon + \param starttime: The time at the start of the animation + \param currenttime: The present time, used to calculate the frame number + \param loop: Whether the animation should loop or not + \param clip: Clip area. */ + virtual void drawIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime=0, u32 currenttime=0, + bool loop=false, const core::rect* clip=0) + { + drawColoredIcon(element, icon, position, starttime, currenttime, loop, clip); + } + + virtual void drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime=0, u32 currenttime=0, + bool loop=false, const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a 2d rectangle. + /** \param element: Pointer to the element which wishes to draw this icon. + This parameter is usually not used by IGUISkin, but can be used for example + by more complex implementations to find out how to draw the part exactly. + \param color: Color of the rectangle to draw. The alpha component specifies how + transparent the rectangle will be. + \param pos: Position of the rectangle. + \param clip: Pointer to rectangle against which the rectangle will be clipped. + If the pointer is null, no clipping will be performed. */ + virtual void draw2DRectangle(IGUIElement* element, const video::SColor &color, + const core::rect& pos, const core::rect* clip = 0); + + + //! get the type of this skin + virtual EGUI_SKIN_TYPE getType() const; + + //! Writes attributes of the object. + //! Implement this to expose the attributes of your scene node animator for + //! scripting languages, editors, debuggers or xml serialization purposes. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const; + + //! Reads attributes of the object. + //! Implement this to set the attributes of your scene node animator for + //! scripting languages, editors, debuggers or xml deserialization purposes. + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0); + + //! gets the colors + virtual void getColors(video::SColor* colors); // ::PATCH: + + private: + + video::SColor Colors[EGDC_COUNT]; + s32 Sizes[EGDS_COUNT]; + u32 Icons[EGDI_COUNT]; + IGUIFont* Fonts[EGDF_COUNT]; + IGUISpriteBank* SpriteBank; + core::stringw Texts[EGDT_COUNT]; + video::IVideoDriver* Driver; + bool UseGradient; + + EGUI_SKIN_TYPE Type; + }; + + #define set3DSkinColors(skin, button_color) \ + { \ + skin->setColor(EGDC_3D_FACE, button_color); \ + skin->setColor(EGDC_3D_DARK_SHADOW, button_color, 0.25f); \ + skin->setColor(EGDC_3D_SHADOW, button_color, 0.5f); \ + skin->setColor(EGDC_3D_LIGHT, button_color); \ + skin->setColor(EGDC_3D_HIGH_LIGHT, button_color, 1.5f); \ + } + + #define getElementSkinColor(color) \ + { \ + if (!Colors) \ + { \ + IGUISkin* skin = Environment->getSkin(); \ + if (skin) \ + return skin->getColor(color); \ + } \ + return Colors[color]; \ + } + + #define setElementSkinColor(which, newColor, shading) \ + { \ + if (!Colors) \ + { \ + Colors = new video::SColor[EGDC_COUNT]; \ + GUISkin* skin = (GUISkin *)Environment->getSkin(); \ + if (skin) \ + skin->getColors(Colors); \ + } \ + Colors[which] = newColor; \ + setShading(Colors[which],shading); \ + } +} // end namespace gui +//! Sets the shading +inline void setShading(video::SColor &color,f32 s) // :PATCH: +{ + if (s < 1.0f) + { + color.setRed(color.getRed() * s); + color.setGreen(color.getGreen() * s); + color.setBlue(color.getBlue() * s); + } + else if (s > 1.0f) + { + s -= 1.0f; + + color.setRed(color.getRed() + (255 - color.getRed()) * s); + color.setGreen(color.getGreen() + (255 - color.getGreen()) * s); + color.setBlue(color.getBlue() + (255 - color.getBlue()) * s); + } +} +} // end namespace irr + + +#endif // _IRR_COMPILE_WITH_GUI_ + +#endif diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index 15aa9bbce..f9cf150ed 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -155,6 +155,8 @@ src/genericobject.cpp src/genericobject.h src/gettext.cpp src/gettext.h +src/gui/guiButton.cpp +src/gui/guiButton.h src/gui/guiChatConsole.cpp src/gui/guiChatConsole.h src/gui/guiConfirmRegistration.cpp @@ -170,6 +172,8 @@ src/gui/guiPasswordChange.cpp src/gui/guiPathSelectMenu.cpp src/gui/guiPathSelectMenu.h src/gui/guiScrollBar.cpp +src/gui/guiSkin.cpp +src/gui/guiSkin.h src/gui/guiTable.cpp src/gui/guiTable.h src/gui/guiVolumeChange.cpp