summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt3
-rw-r--r--src/gui/StyleSpec.h135
-rw-r--r--src/gui/guiButton.cpp649
-rw-r--r--src/gui/guiButton.h310
-rw-r--r--src/gui/guiChatConsole.cpp18
-rw-r--r--src/gui/guiChatConsole.h2
-rw-r--r--src/gui/guiConfirmRegistration.cpp9
-rw-r--r--src/gui/guiConfirmRegistration.h3
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp12
-rw-r--r--src/gui/guiEditBoxWithScrollbar.h6
-rw-r--r--src/gui/guiEngine.cpp19
-rw-r--r--src/gui/guiEngine.h6
-rw-r--r--src/gui/guiFormSpecMenu.cpp864
-rw-r--r--src/gui/guiFormSpecMenu.h46
-rw-r--r--src/gui/guiKeyChangeMenu.cpp13
-rw-r--r--src/gui/guiScrollBar.cpp425
-rw-r--r--src/gui/guiScrollBar.h69
-rw-r--r--src/gui/guiSkin.cpp1084
-rw-r--r--src/gui/guiSkin.h376
-rw-r--r--src/gui/guiTable.cpp9
-rw-r--r--src/gui/guiTable.h3
-rw-r--r--src/gui/guiVolumeChange.cpp4
-rw-r--r--src/gui/intlGUIEditBox.cpp10
-rw-r--r--src/gui/intlGUIEditBox.h4
-rw-r--r--src/gui/mainmenumanager.h16
-rw-r--r--src/gui/modalMenu.cpp2
-rw-r--r--src/gui/touchscreengui.h9
27 files changed, 3842 insertions, 264 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 4bc451825..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
@@ -7,6 +8,8 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
${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/StyleSpec.h b/src/gui/StyleSpec.h
new file mode 100644
index 000000000..29aae0836
--- /dev/null
+++ b/src/gui/StyleSpec.h
@@ -0,0 +1,135 @@
+/*
+Minetest
+Copyright (C) 2019 rubenwardy
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "irrlichttypes_extrabloated.h"
+#include <array>
+
+#pragma once
+
+class StyleSpec
+{
+public:
+ enum Property
+ {
+ TEXTCOLOR,
+ BGCOLOR,
+ NOCLIP,
+ BORDER,
+ BGIMG,
+ BGIMG_PRESSED,
+ ALPHA,
+ NUM_PROPERTIES,
+ NONE
+ };
+
+private:
+ std::array<bool, NUM_PROPERTIES> property_set;
+ std::array<std::string, NUM_PROPERTIES> properties;
+
+public:
+ static Property GetPropertyByName(const std::string &name)
+ {
+ if (name == "textcolor") {
+ return TEXTCOLOR;
+ } else if (name == "bgcolor") {
+ return BGCOLOR;
+ } else if (name == "noclip") {
+ return NOCLIP;
+ } else if (name == "border") {
+ return BORDER;
+ } else if (name == "bgimg") {
+ return BGIMG;
+ } else if (name == "bgimg_pressed") {
+ return BGIMG_PRESSED;
+ } else if (name == "alpha") {
+ return ALPHA;
+ } else {
+ return NONE;
+ }
+ }
+
+ std::string get(Property prop, std::string def) const
+ {
+ const auto &val = properties[prop];
+ return val.empty() ? def : val;
+ }
+
+ void set(Property prop, const std::string &value)
+ {
+ properties[prop] = value;
+ property_set[prop] = true;
+ }
+
+ video::SColor getColor(Property prop, video::SColor def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty()) {
+ return def;
+ }
+
+ parseColorString(val, def, false, 0xFF);
+ return def;
+ }
+
+ video::SColor getColor(Property prop) const
+ {
+ const auto &val = properties[prop];
+ FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
+
+ video::SColor color;
+ parseColorString(val, color, false, 0xFF);
+ return color;
+ }
+
+ bool getBool(Property prop, bool def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty()) {
+ return def;
+ }
+
+ return is_yes(val);
+ }
+
+ inline bool isNotDefault(Property prop) const
+ {
+ return !properties[prop].empty();
+ }
+
+ inline bool hasProperty(Property prop) const { return property_set[prop]; }
+
+ StyleSpec &operator|=(const StyleSpec &other)
+ {
+ for (size_t i = 0; i < NUM_PROPERTIES; i++) {
+ auto prop = (Property)i;
+ if (other.hasProperty(prop)) {
+ set(prop, other.get(prop, ""));
+ }
+ }
+
+ return *this;
+ }
+
+ StyleSpec operator|(const StyleSpec &other) const
+ {
+ StyleSpec newspec = *this;
+ newspec |= other;
+ return newspec;
+ }
+};
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<s32> 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<s32>(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<GUISkin*>(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<s32> pos(buttonCenter);
+ core::rect<s32> sourceRect(ButtonImages[(u32)imageState].SourceRect);
+ if ( sourceRect.getWidth() == 0 && sourceRect.getHeight() == 0 )
+ sourceRect = core::rect<s32>(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<s32>(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<s32> 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<const IGUIElement*>(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<s32>& 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<s32> 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<s32>& 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..63e29ccfc
--- /dev/null
+++ b/src/gui/guiButton.h
@@ -0,0 +1,310 @@
+// 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)
+ 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<s32> rectangle, bool noclip=false);
+
+ //! destructor
+ virtual ~GUIButton();
+
+ //! called if an event happened.
+ virtual bool OnEvent(const SEvent& event) override;
+
+ //! draws the element and its children
+ virtual void draw() override;
+
+ //! 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) override;
+
+ //! Gets the override font (if any)
+ virtual gui::IGUIFont* getOverrideFont() const override;
+
+ //! Get the font which is used right now for drawing
+ virtual gui::IGUIFont* getActiveFont() const override;
+
+ //! 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<s32>& sourceRect=core::rect<s32>(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) override
+ {
+ 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<s32>& pos) override
+ {
+ 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) override
+ {
+ 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<s32>& pos) override
+ {
+ setImage(gui::EGBIS_IMAGE_DOWN, image, pos);
+ }
+
+ //! Sets the sprite bank used by the button
+ virtual void setSpriteBank(gui::IGUISpriteBank* bank=0) override;
+
+ //! 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);
+
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8)
+ void setSprite(gui::EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) override {
+ setSprite(state, index, color, loop, false);
+ }
+#endif
+
+ //! 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) override;
+
+ //! Checks whether the button is a push button
+ virtual bool isPushButton() const override;
+
+ //! Sets the pressed state of the button if this is a pushbutton
+ virtual void setPressed(bool pressed=true) override;
+
+ //! Returns if the button is currently pressed
+ virtual bool isPressed() const override;
+
+ //! Sets if the button should use the skin to draw its border
+ virtual void setDrawBorder(bool border=true) override;
+
+ //! Checks if the button face and border are being drawn
+ virtual bool isDrawingBorder() const override;
+
+ //! Sets if the alpha channel should be used for drawing images on the button (default is false)
+ virtual void setUseAlphaChannel(bool useAlphaChannel=true) override;
+
+ //! Checks if the alpha channel should be used for drawing images on the button
+ virtual bool isAlphaChannelUsed() const override;
+
+ //! Sets if the button should scale the button images to fit
+ virtual void setScaleImage(bool scaleImage=true) override;
+
+ //! Checks whether the button scales the used images
+ virtual bool isScalingImage() const override;
+
+ //! 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 override;
+
+ //! Reads attributes of the element
+ virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) override;
+
+
+
+ void setColor(video::SColor color);
+
+
+ //! Do not drop returned handle
+ static GUIButton* addButton(gui::IGUIEnvironment *environment, const core::rect<s32>& 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<s32>(0,0,0,0))
+ {
+ }
+
+ ButtonImage(const ButtonImage& other) : Texture(0), SourceRect(core::rect<s32>(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<s32> 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/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp
index 1ccb4e6d1..e67fae3c6 100644
--- a/src/gui/guiChatConsole.cpp
+++ b/src/gui/guiChatConsole.cpp
@@ -77,7 +77,7 @@ GUIChatConsole::GUIChatConsole(
m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono);
if (!m_font) {
- errorstream << "GUIChatConsole: Unable to load mono font ";
+ errorstream << "GUIChatConsole: Unable to load mono font" << std::endl;
} else {
core::dimension2d<u32> dim = m_font->getDimension(L"M");
m_fontsize = v2u32(dim.Width, dim.Height);
@@ -139,7 +139,7 @@ f32 GUIChatConsole::getDesiredHeight() const
return m_desired_height_fraction;
}
-void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
+void GUIChatConsole::replaceAndAddToHistory(const std::wstring &line)
{
ChatPrompt& prompt = m_chat_backend->getPrompt();
prompt.addToHistory(prompt.getLine());
@@ -322,9 +322,9 @@ void GUIChatConsole::drawText()
core::rect<s32> destrect(
x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
-
- #if USE_FREETYPE
- // Draw colored text if FreeType is enabled
+#if USE_FREETYPE
+ if (m_font->getType() == irr::gui::EGFT_CUSTOM) {
+ // Draw colored text if FreeType is enabled
irr::gui::CGUITTFont *tmp = dynamic_cast<irr::gui::CGUITTFont *>(m_font);
tmp->draw(
fragment.text,
@@ -333,8 +333,10 @@ void GUIChatConsole::drawText()
false,
false,
&AbsoluteClippingRect);
- #else
- // Otherwise use standard text
+ } else
+#endif
+ {
+ // Otherwise use standard text
m_font->draw(
fragment.text.c_str(),
destrect,
@@ -342,7 +344,7 @@ void GUIChatConsole::drawText()
false,
false,
&AbsoluteClippingRect);
- #endif
+ }
}
}
}
diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h
index ef8a87673..7be40e27c 100644
--- a/src/gui/guiChatConsole.h
+++ b/src/gui/guiChatConsole.h
@@ -60,7 +60,7 @@ public:
f32 getDesiredHeight() const;
// Replace actual line when adding the actual to the history (if there is any)
- void replaceAndAddToHistory(std::wstring line);
+ void replaceAndAddToHistory(const std::wstring &line);
// Change how the cursor looks
void setCursor(
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
index 6e6b7ad16..6fe2a4fc4 100644
--- a/src/gui/guiConfirmRegistration.cpp
+++ b/src/gui/guiConfirmRegistration.cpp
@@ -38,10 +38,10 @@ const int ID_cancel = 265;
GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
- const std::string &address, bool *aborted) :
+ bool *aborted) :
GUIModalMenu(env, parent, id, menumgr),
m_client(client), m_playername(playername), m_password(password),
- m_address(address), m_aborted(aborted)
+ m_aborted(aborted)
{
#ifdef __ANDROID__
m_touchscreen_visible = false;
@@ -62,6 +62,7 @@ void GUIConfirmRegistration::removeChildren()
for (gui::IGUIElement *i : children_copy)
i->remove();
}
+
void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
{
acceptInput();
@@ -119,6 +120,7 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
rect2, true, this, ID_confirmPassword);
e->setPasswordBox(true);
+ Environment->setFocus(e);
}
ypos += 60 * s;
@@ -218,8 +220,7 @@ bool GUIConfirmRegistration::OnEvent(const SEvent &event)
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
- dstream << "GUIConfirmRegistration: Not allowing focus "
- "change."
+ dstream << "GUIConfirmRegistration: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
diff --git a/src/gui/guiConfirmRegistration.h b/src/gui/guiConfirmRegistration.h
index 2f2066c21..42c07e4ed 100644
--- a/src/gui/guiConfirmRegistration.h
+++ b/src/gui/guiConfirmRegistration.h
@@ -32,7 +32,7 @@ public:
GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
- const std::string &address, bool *aborted);
+ bool *aborted);
~GUIConfirmRegistration();
void removeChildren();
@@ -61,7 +61,6 @@ private:
Client *m_client = nullptr;
const std::string &m_playername;
const std::string &m_password;
- const std::string &m_address;
bool *m_aborted = nullptr;
std::wstring m_pass_confirm = L"";
};
diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
index 4a821c7b4..2f909f54f 100644
--- a/src/gui/guiEditBoxWithScrollbar.cpp
+++ b/src/gui/guiEditBoxWithScrollbar.cpp
@@ -13,7 +13,6 @@
#include "porting.h"
#include "Keycodes.h"
-
/*
todo:
optional scrollbars [done]
@@ -76,7 +75,8 @@ GUIEditBoxWithScrollBar::~GUIEditBoxWithScrollBar()
if (m_operator)
m_operator->drop();
- m_vscrollbar->remove();
+ if (m_vscrollbar)
+ m_vscrollbar->drop();
}
@@ -1400,7 +1400,9 @@ void GUIEditBoxWithScrollBar::createVScrollBar()
irr::core::rect<s32> scrollbarrect = m_frame_rect;
scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
- m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
+ m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
+ scrollbarrect, false, true);
+
m_vscrollbar->setVisible(false);
m_vscrollbar->setSmallStep(1);
m_vscrollbar->setLargeStep(1);
@@ -1422,6 +1424,7 @@ void GUIEditBoxWithScrollBar::updateVScrollBar()
if (scrollymax != m_vscrollbar->getMax()) {
// manage a newline or a deleted line
m_vscrollbar->setMax(scrollymax);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
calculateScrollPos();
} else {
// manage a newline or a deleted line
@@ -1436,6 +1439,7 @@ void GUIEditBoxWithScrollBar::updateVScrollBar()
s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
if (scrollymax != m_vscrollbar->getMax()) {
m_vscrollbar->setMax(scrollymax);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
}
if (!m_vscrollbar->isVisible()) {
@@ -1448,10 +1452,10 @@ void GUIEditBoxWithScrollBar::updateVScrollBar()
m_vscroll_pos = 0;
m_vscrollbar->setPos(0);
m_vscrollbar->setMax(1);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
}
}
-
}
//! set true if this editbox is writable
diff --git a/src/gui/guiEditBoxWithScrollbar.h b/src/gui/guiEditBoxWithScrollbar.h
index cedffd82f..77538e2f7 100644
--- a/src/gui/guiEditBoxWithScrollbar.h
+++ b/src/gui/guiEditBoxWithScrollbar.h
@@ -7,7 +7,7 @@
#include "IGUIEditBox.h"
#include "IOSOperator.h"
-#include "IGUIScrollBar.h"
+#include "guiScrollBar.h"
#include <vector>
using namespace irr;
@@ -116,7 +116,7 @@ public:
//! Updates the absolute position, splits text if required
virtual void updateAbsolutePosition();
-
+
virtual void setWritable(bool writable);
//! Change the background color
@@ -187,7 +187,7 @@ protected:
core::rect<s32> m_current_text_rect, m_frame_rect; // temporary values
u32 m_scrollbar_width;
- IGUIScrollBar *m_vscrollbar;
+ GUIScrollBar *m_vscrollbar;
bool m_writable;
bool m_bg_color_used;
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index 6030a5bfb..3107d64cd 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -39,9 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/guiscalingfilter.h"
#include "irrlicht_changes/static_text.h"
-#ifdef __ANDROID__
+#if ENABLE_GLES
#include "client/tile.h"
-#include <GLES/gl.h>
#endif
@@ -78,7 +77,7 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
m_to_delete.insert(name);
-#ifdef __ANDROID__
+#if ENABLE_GLES
video::ITexture *retval = m_driver->findTexture(name.c_str());
if (retval)
return retval;
@@ -391,6 +390,15 @@ void GUIEngine::cloudPostProcess()
}
/******************************************************************************/
+void GUIEngine::setFormspecPrepend(const std::string &fs)
+{
+ if (m_menu) {
+ m_menu->setFormspecPrepend(fs);
+ }
+}
+
+
+/******************************************************************************/
void GUIEngine::drawBackground(video::IVideoDriver *driver)
{
v2u32 screensize = driver->getScreenSize();
@@ -518,7 +526,7 @@ void GUIEngine::drawFooter(video::IVideoDriver *driver)
}
/******************************************************************************/
-bool GUIEngine::setTexture(texture_layer layer, std::string texturepath,
+bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath,
bool tile_image, unsigned int minsize)
{
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
@@ -593,7 +601,7 @@ void GUIEngine::updateTopLeftTextSize()
}
/******************************************************************************/
-s32 GUIEngine::playSound(SimpleSoundSpec spec, bool looped)
+s32 GUIEngine::playSound(const SimpleSoundSpec &spec, bool looped)
{
s32 handle = m_sound_manager->playSound(spec, looped);
return handle;
@@ -611,4 +619,3 @@ unsigned int GUIEngine::queueAsync(const std::string &serialized_func,
{
return m_script->queueAsync(serialized_func, serialized_params);
}
-
diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h
index 409ba94c4..e55531bbc 100644
--- a/src/gui/guiEngine.h
+++ b/src/gui/guiEngine.h
@@ -221,6 +221,8 @@ private:
/** script basefolder */
std::string m_scriptdir = "";
+ void setFormspecPrepend(const std::string &fs);
+
/**
* draw background layer
* @param driver to use for drawing
@@ -247,7 +249,7 @@ private:
* @param layer draw layer to specify texture
* @param texturepath full path of texture to load
*/
- bool setTexture(texture_layer layer, std::string texturepath,
+ bool setTexture(texture_layer layer, const std::string &texturepath,
bool tile_image, unsigned int minsize);
/**
@@ -296,7 +298,7 @@ private:
clouddata m_cloud;
/** start playing a sound and return handle */
- s32 playSound(SimpleSoundSpec spec, bool looped);
+ s32 playSound(const SimpleSoundSpec &spec, bool looped);
/** stop playing a sound started with playSound() */
void stopSound(s32 handle);
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 6c404728f..aee7da869 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 <iterator>
#include <sstream>
#include <limits>
+#include "guiButton.h"
#include "guiFormSpecMenu.h"
#include "guiTable.h"
#include "constants.h"
@@ -66,8 +67,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MY_CHECKGEOM(a,b) \
if (v_geom.size() != 2) { \
- errorstream<< "Invalid pos for element " << a << "specified: \"" \
- << parts[b] << "\"" << std::endl; \
+ errorstream<< "Invalid geometry for element " << a << \
+ "specified: \"" << parts[b] << "\"" << std::endl; \
return; \
}
/*
@@ -86,7 +87,7 @@ inline u32 clamp_u8(s32 value)
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
- std::string formspecPrepend,
+ const std::string &formspecPrepend,
bool remap_dbl_click):
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
m_invmgr(client),
@@ -270,6 +271,25 @@ v2s32 GUIFormSpecMenu::getElementBasePos(bool absolute,
return v2s32(pos_f.X, pos_f.Y);
}
+v2s32 GUIFormSpecMenu::getRealCoordinateBasePos(bool absolute,
+ const std::vector<std::string> &v_pos)
+{
+ v2f32 pos_f = v2f32(0.0f, 0.0f);
+
+ pos_f.X += stof(v_pos[0]) + pos_offset.X;
+ pos_f.Y += stof(v_pos[1]) + pos_offset.Y;
+
+ if (absolute)
+ return v2s32(pos_f.X * imgsize.X + AbsoluteRect.UpperLeftCorner.X,
+ pos_f.Y * imgsize.Y + AbsoluteRect.UpperLeftCorner.Y);
+ return v2s32(pos_f.X * imgsize.X, pos_f.Y * imgsize.Y);
+}
+
+v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector<std::string> &v_geom)
+{
+ return v2s32(stof(v_geom[0]) * imgsize.X, stof(v_geom[1]) * imgsize.Y);
+}
+
void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,',');
@@ -306,8 +326,8 @@ void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &elemen
parts[1] = parts[1].substr(0, parts[1].find(';'));
container_stack.push(pos_offset);
- pos_offset.X += MYMAX(0, stof(parts[0]));
- pos_offset.Y += MYMAX(0, stof(parts[1]));
+ pos_offset.X += stof(parts[0]);
+ pos_offset.Y += stof(parts[1]);
return;
}
errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl;
@@ -348,13 +368,19 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
InventoryLocation loc;
- if(location == "context" || location == "current_name")
+ if (location == "context" || location == "current_name")
loc = m_current_inventory_location;
else
loc.deSerialize(location);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
+
+ if (data->real_coordinates)
+ pos = getRealCoordinateBasePos(true, v_pos);
+ else
+ pos = getElementBasePos(true, &v_pos);
+
geom.X = stoi(v_geom[0]);
geom.Y = stoi(v_geom[1]);
@@ -369,7 +395,7 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
if(!data->explicit_size)
warningstream<<"invalid use of list without a size[] element"<<std::endl;
- m_inventorylists.emplace_back(loc, listname, pos, geom, start_i);
+ m_inventorylists.emplace_back(loc, listname, pos, geom, start_i, data->real_coordinates);
return;
}
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
@@ -430,20 +456,37 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
MY_CHECKPOS("checkbox",0);
- v2s32 pos = getElementBasePos(false, &v_pos);
-
bool fselected = false;
if (selected == "true")
fselected = true;
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
- s32 spacing = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH) + 7;
+ const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str());
+ s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH);
+ s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2;
+
+ v2s32 pos;
+ core::rect<s32> rect;
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y + ((imgsize.Y / 2) - m_btn_height),
- pos.X + m_font->getDimension(wlabel.c_str()).Width + spacing,
- pos.Y + ((imgsize.Y / 2) + m_btn_height));
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ rect = core::rect<s32>(
+ pos.X,
+ pos.Y - y_center,
+ pos.X + label_size.Width + cb_size + 7,
+ pos.Y + y_center
+ );
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ rect = core::rect<s32>(
+ pos.X,
+ pos.Y + imgsize.Y / 2 - y_center,
+ pos.X + label_size.Width + cb_size + 7,
+ pos.Y + imgsize.Y / 2 + y_center
+ );
+ }
FieldSpec spec(
name,
@@ -457,6 +500,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
spec.fid, spec.flabel.c_str());
+ auto style = getStyleForElement("checkbox", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
@@ -474,24 +520,25 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
if (parts.size() >= 5) {
std::vector<std::string> v_pos = split(parts[0],',');
- std::vector<std::string> v_dim = split(parts[1],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
std::string name = parts[3];
std::string value = parts[4];
MY_CHECKPOS("scrollbar",0);
+ MY_CHECKGEOM("scrollbar",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
+ v2s32 dim;
- if (v_dim.size() != 2) {
- errorstream<< "Invalid size for element " << "scrollbar"
- << "specified: \"" << parts[1] << "\"" << std::endl;
- return;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ dim = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ dim.X = stof(v_geom[0]) * spacing.X;
+ dim.Y = stof(v_geom[1]) * spacing.Y;
}
- v2s32 dim;
- dim.X = stof(v_dim[0]) * spacing.X;
- dim.Y = stof(v_dim[1]) * spacing.Y;
-
core::rect<s32> rect =
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
@@ -512,6 +559,9 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
gui::IGUIScrollBar* e =
Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
+ auto style = getStyleForElement("scrollbar", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
e->setMax(1000);
e->setMin(0);
e->setPos(stoi(parts[4]));
@@ -539,10 +589,17 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
MY_CHECKPOS("image", 0);
MY_CHECKGEOM("image", 1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)imgsize.X;
- geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
if (!data->explicit_size)
warningstream<<"invalid use of image without a size[] element"<<std::endl;
@@ -580,10 +637,17 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen
MY_CHECKPOS("itemimage",0);
MY_CHECKGEOM("itemimage",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)imgsize.X;
- geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
if(!data->explicit_size)
warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
@@ -609,14 +673,23 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
MY_CHECKPOS("button",0);
MY_CHECKGEOM("button",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ core::rect<s32> rect;
- core::rect<s32> rect =
- core::rect<s32>(pos.X, pos.Y - m_btn_height,
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
+ pos.Y+geom.Y);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+
+ rect = core::rect<s32>(pos.X, pos.Y - m_btn_height,
pos.X + geom.X, pos.Y + m_btn_height);
+ }
if(!data->explicit_size)
warningstream<<"invalid use of button without a size[] element"<<std::endl;
@@ -632,8 +705,38 @@ 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());
+
+ auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
+ if (style.isNotDefault(StyleSpec::BGCOLOR)) {
+ e->setColor(style.getColor(StyleSpec::BGCOLOR));
+ }
+ if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
+ }
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+
+ if (style.isNotDefault(StyleSpec::BGIMG)) {
+ std::string image_name = style.get(StyleSpec::BGIMG, "");
+ std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, "");
+
+ video::ITexture *texture = 0;
+ video::ITexture *pressed_texture = 0;
+ texture = m_tsrc->getTexture(image_name);
+ if (!pressed_image_name.empty())
+ pressed_texture = m_tsrc->getTexture(pressed_image_name);
+ else
+ pressed_texture = texture;
+
+ e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
+ e->setImage(guiScalingImageButton(
+ Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ e->setPressedImage(guiScalingImageButton(
+ Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+ e->setScaleImage(true);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
@@ -649,9 +752,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme
{
std::vector<std::string> parts = split(element,';');
- if (((parts.size() == 3) || (parts.size() == 4)) ||
- ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
- {
+ if ((parts.size() >= 3 && parts.size() <= 5) ||
+ (parts.size() > 5 && m_formspec_version > FORMSPEC_API_VERSION)) {
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
std::string name = unescape_string(parts[2]);
@@ -659,25 +761,58 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme
MY_CHECKPOS("background",0);
MY_CHECKGEOM("background",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
- pos.X -= (spacing.X - (float)imgsize.X) / 2;
- pos.Y -= (spacing.Y - (float)imgsize.Y) / 2;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ pos.X -= (spacing.X - (float)imgsize.X) / 2;
+ pos.Y -= (spacing.Y - (float)imgsize.Y) / 2;
+
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
bool clip = false;
- if (parts.size() == 4 && is_yes(parts[3])) {
- pos.X = stoi(v_pos[0]); //acts as offset
- pos.Y = stoi(v_pos[1]); //acts as offset
+ if (parts.size() >= 4 && is_yes(parts[3])) {
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos) * -1;
+ geom = v2s32(0, 0);
+ } else {
+ pos.X = stoi(v_pos[0]); //acts as offset
+ pos.Y = stoi(v_pos[1]);
+ }
clip = true;
}
+ core::rect<s32> middle;
+ if (parts.size() >= 5) {
+ std::vector<std::string> v_middle = split(parts[4], ',');
+ if (v_middle.size() == 1) {
+ s32 x = stoi(v_middle[0]);
+ middle.UpperLeftCorner = core::vector2di(x, x);
+ middle.LowerRightCorner = core::vector2di(-x, -x);
+ } else if (v_middle.size() == 2) {
+ s32 x = stoi(v_middle[0]);
+ s32 y = stoi(v_middle[1]);
+ middle.UpperLeftCorner = core::vector2di(x, y);
+ middle.LowerRightCorner = core::vector2di(-x, -y);
+ // `-x` is interpreted as `w - x`
+ } else if (v_middle.size() == 4) {
+ middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1]));
+ middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3]));
+ } else {
+ warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl;
+ }
+ }
+
if (!data->explicit_size && !clip)
warningstream << "invalid use of unclipped background without a size[] element" << std::endl;
- m_backgrounds.emplace_back(name, pos, geom, clip);
+ m_backgrounds.emplace_back(name, pos, geom, middle, clip);
return;
}
@@ -736,10 +871,17 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
MY_CHECKPOS("table",0);
MY_CHECKGEOM("table",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
@@ -773,6 +915,9 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection));
+ auto style = getStyleForElement("table", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
return;
@@ -803,11 +948,17 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
MY_CHECKPOS("textlist",0);
MY_CHECKGEOM("textlist",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
@@ -841,6 +992,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection));
+ auto style = getStyleForElement("textlist", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
return;
@@ -864,12 +1018,29 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
MY_CHECKPOS("dropdown",0);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
+ v2s32 geom;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ std::vector<std::string> v_geom = split(parts[1],',');
+
+ if (v_geom.size() == 1)
+ v_geom.emplace_back("1");
+
+ MY_CHECKGEOM("dropdown",1);
- s32 width = stof(parts[1]) * spacing.Y;
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+
+ s32 width = stof(parts[1]) * spacing.Y;
- core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
- pos.X + width, pos.Y + (m_btn_height * 2));
+ rect = core::rect<s32>(pos.X, pos.Y,
+ pos.X + width, pos.Y + (m_btn_height * 2));
+ }
FieldSpec spec(
name,
@@ -896,6 +1067,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
if (!str_initial_selection.empty())
e->setSelected(stoi(str_initial_selection)-1);
+ auto style = getStyleForElement("dropdown", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_fields.push_back(spec);
m_dropdowns.emplace_back(spec, std::vector<std::string>());
@@ -934,15 +1108,22 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
MY_CHECKPOS("pwdfield",0);
MY_CHECKGEOM("pwdfield",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- pos -= padding;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
- pos.Y -= m_btn_height;
- geom.Y = m_btn_height*2;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ pos -= padding;
+
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= m_btn_height;
+ geom.Y = m_btn_height*2;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
@@ -972,6 +1153,11 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
e->setPasswordBox(true,L'*');
+ auto style = getStyleForElement("pwdfield", name, "field");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
@@ -1022,12 +1208,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
true, Environment, this, spec.fid, rect, is_editable, is_multiline);
e->drop();
} else {
- if (is_multiline)
+ if (is_multiline) {
e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true,
Environment, this, spec.fid, rect, is_editable, true);
- else if (is_editable)
+ e->drop();
+ } else if (is_editable) {
e = Environment->addEditBox(spec.fdefault.c_str(), rect, true,
this, spec.fid);
+ }
}
if (e) {
@@ -1048,6 +1236,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
}
+
+ auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+ if (style.get(StyleSpec::BGCOLOR, "") == "transparent") {
+ e->setDrawBackground(false);
+ }
}
if (!spec.flabel.empty()) {
@@ -1117,23 +1313,29 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>&
MY_CHECKPOS(type,0);
MY_CHECKGEOM(type,1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- pos -= padding;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ pos -= padding;
- if (type == "textarea")
- {
- geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
- pos.Y += m_btn_height;
- }
- else
- {
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
- pos.Y -= m_btn_height;
- geom.Y = m_btn_height*2;
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+
+ if (type == "textarea")
+ {
+ geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
+ pos.Y += m_btn_height;
+ }
+ else
+ {
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= m_btn_height;
+ geom.Y = m_btn_height*2;
+ }
}
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
@@ -1197,46 +1399,79 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
MY_CHECKPOS("label",0);
- v2s32 pos = getElementBasePos(false, nullptr);
- pos.X += stof(v_pos[0]) * spacing.X;
- pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y;
-
if(!data->explicit_size)
warningstream<<"invalid use of label without a size[] element"<<std::endl;
std::vector<std::string> lines = split(text, '\n');
for (unsigned int i = 0; i != lines.size(); i++) {
- // Lines are spaced at the nominal distance of
- // 2/5 inventory slot, even if the font doesn't
- // quite match that. This provides consistent
- // form layout, at the expense of sometimes
- // having sub-optimal spacing for the font.
- // We multiply by 2 and then divide by 5, rather
- // than multiply by 0.4, to get exact results
- // in the integer cases: 0.4 is not exactly
- // representable in binary floating point.
- s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
- std::wstring wlabel = utf8_to_wide(unescape_string(lines[i]));
- core::rect<s32> rect = core::rect<s32>(
- pos.X, posy - m_btn_height,
- pos.X + m_font->getDimension(wlabel.c_str()).Width,
- posy + m_btn_height);
+ std::wstring wlabel_colors = translate_string(
+ utf8_to_wide(unescape_string(lines[i])));
+ // Without color escapes to get the font dimensions
+ std::wstring wlabel_plain = unescape_enriched(wlabel_colors);
+
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ // Lines are spaced at the distance of 1/2 imgsize.
+ // This alows lines that line up with the new elements
+ // easily without sacrificing good line distance. If
+ // it was one whole imgsize, it would have too much
+ // spacing.
+ v2s32 pos = getRealCoordinateBasePos(false, v_pos);
+
+ // Labels are positioned by their center, not their top.
+ pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2);
+
+ rect = core::rect<s32>(
+ pos.X, pos.Y,
+ pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
+ pos.Y + imgsize.Y);
+
+ } else {
+ // Lines are spaced at the nominal distance of
+ // 2/5 inventory slot, even if the font doesn't
+ // quite match that. This provides consistent
+ // form layout, at the expense of sometimes
+ // having sub-optimal spacing for the font.
+ // We multiply by 2 and then divide by 5, rather
+ // than multiply by 0.4, to get exact results
+ // in the integer cases: 0.4 is not exactly
+ // representable in binary floating point.
+
+ v2s32 pos = getElementBasePos(false, nullptr);
+ pos.X += stof(v_pos[0]) * spacing.X;
+ pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y;
+
+ pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0;
+
+ rect = core::rect<s32>(
+ pos.X, pos.Y - m_btn_height,
+ pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
+ pos.Y + m_btn_height);
+ }
+
FieldSpec spec(
"",
- wlabel,
+ wlabel_colors,
L"",
258+m_fields.size()
);
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
spec.flabel.c_str(), rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
+
+ auto style = getStyleForElement("label", spec.fname);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
m_fields.push_back(spec);
}
return;
}
- errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid label element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
@@ -1252,15 +1487,35 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
MY_CHECKPOS("vertlabel",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ // Vertlabels are positioned by center, not left.
+ pos.X -= imgsize.X / 2;
+
+ // We use text.length + 1 because without it, the rect
+ // isn't quite tall enough and cuts off the text.
+ rect = core::rect<s32>(pos.X, pos.Y,
+ pos.X + imgsize.X,
+ pos.Y + font_line_height(m_font) *
+ (text.length() + 1));
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+
+ // As above, the length must be one longer. The width of
+ // the rect (15 pixels) seems rather arbitrary, but
+ // changing it might break something.
+ rect = core::rect<s32>(
+ pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
pos.X+15, pos.Y +
- font_line_height(m_font)
- * (text.length()+1)
- +((imgsize.Y/2)- m_btn_height));
- //actually text.length() would be correct but adding +1 avoids to break all mods
+ font_line_height(m_font) *
+ (text.length() + 1) +
+ ((imgsize.Y/2) - m_btn_height));
+ }
if(!data->explicit_size)
warningstream<<"invalid use of label without a size[] element"<<std::endl;
@@ -1278,9 +1533,14 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
L"",
258+m_fields.size()
);
- gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
+ gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+ e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+
+ auto style = getStyleForElement("vertlabel", spec.fname, "label");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
m_fields.push_back(spec);
return;
}
@@ -1304,11 +1564,6 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
MY_CHECKPOS("imagebutton",0);
MY_CHECKGEOM("imagebutton",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
-
bool noclip = false;
bool drawborder = true;
std::string pressed_image_name;
@@ -1324,9 +1579,22 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
pressed_image_name = parts[7];
}
- core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+ v2s32 pos;
+ v2s32 geom;
- if(!data->explicit_size)
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
+ }
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
+ pos.Y+geom.Y);
+
+ if (!data->explicit_size)
warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
image_name = unescape_string(image_name);
@@ -1341,7 +1609,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
258+m_fields.size()
);
spec.ftype = f_Button;
- if(type == "image_button_exit")
+ if (type == "image_button_exit")
spec.is_exit = true;
video::ITexture *texture = 0;
@@ -1358,14 +1626,21 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
Environment->setFocus(e);
}
- e->setUseAlphaChannel(true);
+ auto style = getStyleForElement("image_button", spec.fname);
+
+ e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
e->setImage(guiScalingImageButton(
Environment->getVideoDriver(), texture, geom.X, geom.Y));
e->setPressedImage(guiScalingImageButton(
Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
e->setScaleImage(true);
- e->setNotClipped(noclip);
- e->setDrawBorder(drawborder);
+ if (parts.size() >= 7) {
+ e->setNotClipped(noclip);
+ e->setDrawBorder(drawborder);
+ } else {
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ }
m_fields.push_back(spec);
return;
@@ -1376,25 +1651,43 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
- if (((parts.size() == 4) || (parts.size() == 6)) ||
- ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+ if (((parts.size() == 4) || (parts.size() == 6)) || (parts.size() == 7 &&
+ data->real_coordinates) || ((parts.size() > 6) &&
+ (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
- std::string name = parts[1];
- std::vector<std::string> buttons = split(parts[2],',');
- std::string str_index = parts[3];
+
+ // If we're using real coordinates, add an extra field for height.
+ // Width is not here because tabs are the width of the text, and
+ // there's no reason to change that.
+ unsigned int i = 0;
+ std::vector<std::string> v_geom = {"1", "0.75"}; // Dummy width and default height
+ bool auto_width = true;
+ if (parts.size() == 7) {
+ i++;
+
+ v_geom = split(parts[1], ',');
+ if (v_geom.size() == 1)
+ v_geom.insert(v_geom.begin(), "1"); // Dummy value
+ else
+ auto_width = false;
+ }
+
+ std::string name = parts[i+1];
+ std::vector<std::string> buttons = split(parts[i+2], ',');
+ std::string str_index = parts[i+3];
bool show_background = true;
bool show_border = true;
- int tab_index = stoi(str_index) -1;
+ int tab_index = stoi(str_index) - 1;
- MY_CHECKPOS("tabheader",0);
+ MY_CHECKPOS("tabheader", 0);
- if (parts.size() == 6) {
- if (parts[4] == "true")
+ if (parts.size() == 6 + i) {
+ if (parts[4+i] == "true")
show_background = false;
- if (parts[5] == "false")
+ if (parts[5+i] == "false")
show_border = false;
}
@@ -1408,15 +1701,26 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
spec.ftype = f_TabHeader;
v2s32 pos;
- {
+ v2s32 geom;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ geom = getRealCoordinateGeometry(v_geom);
+ pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top.
+ if (auto_width)
+ geom.X = DesiredRect.getWidth(); // Set automatic width
+
+ MY_CHECKGEOM("tabheader", 1);
+ } else {
v2f32 pos_f = pos_offset * spacing;
pos_f.X += stof(v_pos[0]) * spacing.X;
pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2;
pos = v2s32(pos_f.X, pos_f.Y);
+
+ geom.Y = m_btn_height * 2;
+ geom.X = DesiredRect.getWidth();
}
- v2s32 geom;
- geom.X = DesiredRect.getWidth();
- geom.Y = m_btn_height*2;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
pos.Y+geom.Y);
@@ -1425,17 +1729,22 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
show_background, show_border, spec.fid);
e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
- e->setTabHeight(m_btn_height*2);
+ e->setTabHeight(geom.Y);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
- e->setNotClipped(true);
+ auto style = getStyleForElement("tabheader", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
for (const std::string &button : buttons) {
- e->addTab(unescape_translate(unescape_string(
+ auto tab = e->addTab(unescape_translate(unescape_string(
utf8_to_wide(button))).c_str(), -1);
+ if (style.isNotDefault(StyleSpec::BGCOLOR))
+ tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR));
+
+ tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
}
if ((tab_index >= 0) &&
@@ -1476,10 +1785,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
MY_CHECKPOS("itemimagebutton",0);
MY_CHECKGEOM("itemimagebutton",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
@@ -1504,6 +1820,10 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
+ auto style = getStyleForElement("item_image_button", spec.fname, "image_button");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
@@ -1513,7 +1833,11 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
spec.rect=rect;
m_fields.push_back(spec);
- pos = getElementBasePos(true, &v_pos);
+ if (data->real_coordinates)
+ pos = getRealCoordinateBasePos(true, v_pos);
+ else
+ pos = getElementBasePos(true, &v_pos);
+
m_itemimages.emplace_back("", item_name, e, pos, geom);
m_static_texts.emplace_back(utf8_to_wide(label), rect, e);
return;
@@ -1534,10 +1858,17 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
MY_CHECKPOS("box",0);
MY_CHECKGEOM("box",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
video::SColor tmp_color;
@@ -1640,13 +1971,20 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
std::vector<std::string> v_pos = split(parts[0], ',');
std::vector<std::string> v_geom = split(parts[1], ',');
- MY_CHECKPOS("tooltip", 0);
+ MY_CHECKPOS("tooltip", 0);
MY_CHECKGEOM("tooltip", 1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
irr::core::rect<s32> rect(pos, pos + geom);
m_tooltip_rects.emplace_back(rect, spec);
@@ -1772,12 +2110,70 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
<< "'" << std::endl;
}
+bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 2) {
+ errorstream << "Invalid style element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ std::string selector = trim(parts[0]);
+ if (selector.empty()) {
+ errorstream << "Invalid style element (Selector required): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ StyleSpec spec;
+
+ for (size_t i = 1; i < parts.size(); i++) {
+ size_t equal_pos = parts[i].find('=');
+ if (equal_pos == std::string::npos) {
+ errorstream << "Invalid style element (Property missing value): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ std::string propname = trim(parts[i].substr(0, equal_pos));
+ std::string value = trim(unescape_string(parts[i].substr(equal_pos + 1)));
+
+ std::transform(propname.begin(), propname.end(), propname.begin(), ::tolower);
+
+ StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname);
+ if (prop == StyleSpec::NONE) {
+ if (property_warned.find(propname) != property_warned.end()) {
+ warningstream << "Invalid style element (Unknown property " << propname << "): '"
+ << element
+ << "'" << std::endl;
+ property_warned.insert(propname);
+ }
+ return false;
+ }
+
+ spec.set(prop, value);
+ }
+
+ if (style_type) {
+ theme_by_type[selector] |= spec;
+ } else {
+ theme_by_name[selector] |= spec;
+ }
+
+ return true;
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
if (element.empty())
return;
+ if (parseVersionDirect(element))
+ return;
+
std::vector<std::string> parts = split(element,'[');
// ugly workaround to keep compatibility
@@ -1837,8 +2233,8 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
- if (type == "background") {
- parseBackground(data,description);
+ if (type == "background" || type == "background9") {
+ parseBackground(data, description);
return;
}
@@ -1932,6 +2328,21 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "real_coordinates") {
+ data->real_coordinates = is_yes(description);
+ return;
+ }
+
+ if (type == "style") {
+ parseStyle(data, description, false);
+ return;
+ }
+
+ if (type == "style_type") {
+ parseStyle(data, description, true);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
@@ -2002,6 +2413,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_inventory_rings.clear();
m_static_texts.clear();
m_dropdowns.clear();
+ theme_by_name.clear();
+ theme_by_type.clear();
m_bgfullscreen = false;
@@ -2096,6 +2509,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
break;
}
+ /* Copy of the "real_coordinates" element for after the form size. */
+ mydata.real_coordinates = m_formspec_version >= 2;
+ for (; i < elements.size(); i++) {
+ std::vector<std::string> parts = split(elements[i], '[');
+ std::string name = trim(parts[0]);
+ if (name != "real_coordinates" || parts.size() != 2)
+ break; // Invalid format
+
+ mydata.real_coordinates = is_yes(trim(parts[1]));
+ }
+
if (mydata.explicit_size) {
// compute scaling for specified form size
if (m_lock) {
@@ -2186,10 +2610,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_font = g_fontengine->getFont();
- mydata.size = v2s32(
- padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
- padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
- );
+ if (mydata.real_coordinates) {
+ mydata.size = v2s32(
+ mydata.invsize.X*imgsize.X,
+ mydata.invsize.Y*imgsize.Y
+ );
+ } else {
+ mydata.size = v2s32(
+ padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
+ padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
+ );
+ }
+
DesiredRect = mydata.rect = core::rect<s32>(
(s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X,
(s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y,
@@ -2221,9 +2653,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
pos_offset = v2f32();
if (enable_prepends) {
+ // Backup the coordinates so that prepends can use the coordinates of choice.
+ bool rc_backup = mydata.real_coordinates;
+ u16 version_backup = m_formspec_version;
+ mydata.real_coordinates = false; // Old coordinates by default.
+
std::vector<std::string> prepend_elements = split(m_formspec_prepend, ']');
for (const auto &element : prepend_elements)
parseElement(&mydata, element);
+
+ m_formspec_version = version_backup;
+ mydata.real_coordinates = rc_backup; // Restore coordinates
}
for (; i< elements.size(); i++) {
@@ -2313,8 +2753,16 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
s32 item_i = i + s.start_item_i;
- s32 x = (i%s.geom.X) * spacing.X;
- s32 y = (i/s.geom.X) * spacing.Y;
+
+ s32 x;
+ s32 y;
+ if (s.real_coordinates) {
+ x = (i%s.geom.X) * (imgsize.X * 1.25);
+ y = (i/s.geom.X) * (imgsize.Y * 1.25);
+ } else {
+ x = (i%s.geom.X) * spacing.X;
+ y = (i/s.geom.X) * spacing.Y;
+ }
v2s32 p0(x,y);
core::rect<s32> rect = imgrect + s.pos + p0;
if(rect.isPointInside(p))
@@ -2356,8 +2804,15 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
if (item_i >= (s32)ilist->getSize())
break;
- s32 x = (i%s.geom.X) * spacing.X;
- s32 y = (i/s.geom.X) * spacing.Y;
+ s32 x;
+ s32 y;
+ if (s.real_coordinates) {
+ x = (i%s.geom.X) * (imgsize.X * 1.25);
+ y = (i/s.geom.X) * (imgsize.Y * 1.25);
+ } else {
+ x = (i%s.geom.X) * spacing.X;
+ y = (i/s.geom.X) * spacing.Y;
+ }
v2s32 p(x,y);
core::rect<s32> rect = imgrect + s.pos + p;
ItemStack item = ilist->getItem(item_i);
@@ -2401,37 +2856,23 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
}
if (layer == 1) {
- // Draw item stack
if (selected)
item.takeItem(m_selected_amount);
if (!item.empty()) {
+ // Draw item stack
drawItemStack(driver, m_font, item,
rect, &AbsoluteClippingRect, m_client,
rotation_kind);
- }
-
- // Draw tooltip
- std::wstring tooltip_text;
- if (hovering && !m_selected_item) {
- const std::string &desc = item.metadata.getString("description");
- if (desc.empty())
- tooltip_text =
- utf8_to_wide(item.getDefinition(m_client->idef()).description);
- else
- tooltip_text = utf8_to_wide(desc);
-
- if (!item.name.empty()) {
- if (tooltip_text.empty())
- tooltip_text = utf8_to_wide(item.name);
- else if (m_tooltip_append_itemname)
- tooltip_text += utf8_to_wide(" [" + item.name + "]");
+ // Draw tooltip
+ if (hovering && !m_selected_item) {
+ std::string tooltip = item.getDescription(m_client->idef());
+ if (m_tooltip_append_itemname)
+ tooltip += "\n[" + item.name + "]";
+ showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color,
+ m_default_tooltip_bgcolor);
}
}
- if (!tooltip_text.empty()) {
- showTooltip(tooltip_text, m_default_tooltip_color,
- m_default_tooltip_bgcolor);
- }
}
}
}
@@ -2510,6 +2951,8 @@ void GUIFormSpecMenu::drawMenu()
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
+ // Middle rect for 9-slicing
+ core::rect<s32> middle = spec.middle;
if (spec.clip) {
core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
@@ -2519,12 +2962,23 @@ void GUIFormSpecMenu::drawMenu()
AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
}
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- draw2DImageFilterScaled(driver, texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ if (middle.getArea() == 0) {
+ const video::SColor color(255, 255, 255, 255);
+ const video::SColor colors[] = {color, color, color, color};
+ draw2DImageFilterScaled(driver, texture, rect,
+ core::rect<s32>(core::position2d<s32>(0, 0),
+ core::dimension2di(texture->getOriginalSize())),
+ NULL/*&AbsoluteClippingRect*/, colors, true);
+ } else {
+ // `-x` is interpreted as `w - x`
+ if (middle.LowerRightCorner.X < 0) {
+ middle.LowerRightCorner.X += texture->getOriginalSize().Width;
+ }
+ if (middle.LowerRightCorner.Y < 0) {
+ middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
+ }
+ draw2DImage9Slice(driver, texture, rect, middle);
+ }
} else {
errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
errorstream << "\t" << spec.name << std::endl;
@@ -3406,7 +3860,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Mouse has been moved and rmb is down and mouse pointer just
// entered a new inventory field (checked in the entry-if, this
// is the only action here that is generated by mouse movement)
- if (m_selected_item && s.isValid()) {
+ if (m_selected_item && s.isValid() && s.listname != "craftpreview") {
// Move 1 item
// TODO: middle mouse to move 10 items might be handy
if (m_auto_place) {
@@ -3740,3 +4194,27 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
}
return L"";
}
+
+StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type,
+ const std::string &name, const std::string &parent_type) {
+ StyleSpec ret;
+
+ if (!parent_type.empty()) {
+ auto it = theme_by_type.find(parent_type);
+ if (it != theme_by_type.end()) {
+ ret |= it->second;
+ }
+ }
+
+ auto it = theme_by_type.find(type);
+ if (it != theme_by_type.end()) {
+ ret |= it->second;
+ }
+
+ it = theme_by_name.find(name);
+ if (it != theme_by_name.end()) {
+ ret |= it->second;
+ }
+
+ return ret;
+}
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index d75a108d4..46df0930c 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <utility>
#include <stack>
+#include <unordered_set>
#include "irrlichttypes_extrabloated.h"
#include "inventorymanager.h"
@@ -30,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/joystick_controller.h"
#include "util/string.h"
#include "util/enriched_string.h"
+#include "StyleSpec.h"
class InventoryManager;
class ISimpleTextureSource;
@@ -99,12 +101,14 @@ class GUIFormSpecMenu : public GUIModalMenu
ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
- v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
+ v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i,
+ bool a_real_coordinates):
inventoryloc(a_inventoryloc),
listname(a_listname),
pos(a_pos),
geom(a_geom),
- start_item_i(a_start_item_i)
+ start_item_i(a_start_item_i),
+ real_coordinates(a_real_coordinates)
{
}
@@ -113,6 +117,7 @@ class GUIFormSpecMenu : public GUIModalMenu
v2s32 pos;
v2s32 geom;
s32 start_item_i;
+ bool real_coordinates;
};
struct ListRingSpec
@@ -177,6 +182,18 @@ class GUIFormSpecMenu : public GUIModalMenu
}
ImageDrawSpec(const std::string &a_name,
+ const v2s32 &a_pos, const v2s32 &a_geom, const core::rect<s32> &middle, bool clip=false):
+ name(a_name),
+ parent_button(NULL),
+ pos(a_pos),
+ geom(a_geom),
+ middle(middle),
+ scale(true),
+ clip(clip)
+ {
+ }
+
+ ImageDrawSpec(const std::string &a_name,
const v2s32 &a_pos):
name(a_name),
parent_button(NULL),
@@ -191,6 +208,7 @@ class GUIFormSpecMenu : public GUIModalMenu
gui::IGUIButton *parent_button;
v2s32 pos;
v2s32 geom;
+ core::rect<s32> middle;
bool scale;
bool clip;
};
@@ -287,7 +305,7 @@ public:
ISimpleTextureSource *tsrc,
IFormSource* fs_src,
TextDest* txt_dst,
- std::string formspecPrepend,
+ const std::string &formspecPrepend,
bool remap_dbl_click = true);
~GUIFormSpecMenu();
@@ -304,6 +322,11 @@ public:
regenerateGui(m_screensize_old);
}
+ const InventoryLocation &getFormspecLocation()
+ {
+ return m_current_inventory_location;
+ }
+
void setFormspecPrepend(const std::string &formspecPrepend)
{
m_formspec_prepend = formspecPrepend;
@@ -376,6 +399,16 @@ protected:
std::string getNameByID(s32 id);
v2s32 getElementBasePos(bool absolute,
const std::vector<std::string> *v_pos);
+ v2s32 getRealCoordinateBasePos(bool absolute,
+ const std::vector<std::string> &v_pos);
+ v2s32 getRealCoordinateGeometry(const std::vector<std::string> &v_geom);
+
+ std::unordered_map<std::string, StyleSpec> theme_by_type;
+ std::unordered_map<std::string, StyleSpec> theme_by_name;
+ std::unordered_set<std::string> property_warned;
+
+ StyleSpec getStyleForElement(const std::string &type,
+ const std::string &name="", const std::string &parent_type="");
v2s32 padding;
v2f32 spacing;
@@ -439,12 +472,13 @@ protected:
private:
IFormSource *m_form_src;
TextDest *m_text_dst;
- u32 m_formspec_version = 0;
+ u16 m_formspec_version = 1;
std::string m_focused_element = "";
JoystickController *m_joystick;
typedef struct {
bool explicit_size;
+ bool real_coordinates;
v2f invsize;
v2s32 size;
v2f32 offset;
@@ -512,6 +546,7 @@ private:
void parsePosition(parserData *data, const std::string &element);
bool parseAnchorDirect(parserData *data, const std::string &element);
void parseAnchor(parserData *data, const std::string &element);
+ bool parseStyle(parserData *data, const std::string &element, bool style_type);
void tryClose();
@@ -542,7 +577,6 @@ private:
* and the default value for the setting is true.
*/
bool m_remap_dbl_click;
-
};
class FormspecFormSource: public IFormSource
@@ -557,7 +591,7 @@ public:
void setForm(const std::string &formspec)
{
- m_formspec = FORMSPEC_VERSION_STRING + formspec;
+ m_formspec = formspec;
}
const std::string &getForm() const
diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp
index 1a41e5828..ca331a7d4 100644
--- a/src/gui/guiKeyChangeMenu.cpp
+++ b/src/gui/guiKeyChangeMenu.cpp
@@ -50,7 +50,7 @@ enum
GUI_ID_KEY_FAST_BUTTON,
GUI_ID_KEY_JUMP_BUTTON,
GUI_ID_KEY_NOCLIP_BUTTON,
- GUI_ID_KEY_CINEMATIC_BUTTON,
+ GUI_ID_KEY_PITCH_MOVE,
GUI_ID_KEY_CHAT_BUTTON,
GUI_ID_KEY_CMD_BUTTON,
GUI_ID_KEY_CMD_LOCAL_BUTTON,
@@ -119,9 +119,9 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
const float s = m_gui_scale;
DesiredRect = core::rect<s32>(
- screensize.X / 2 - 745 * s / 2,
+ screensize.X / 2 - 835 * s / 2,
screensize.Y / 2 - 430 * s / 2,
- screensize.X / 2 + 745 * s / 2,
+ screensize.X / 2 + 835 * s / 2,
screensize.Y / 2 + 430 * s / 2
);
recalculateAbsolutePosition(false);
@@ -155,13 +155,13 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 100 * s, 30 * s);
- rect += topleft + v2s32(offset.X + 120 * s, offset.Y - 5 * s);
+ rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
const wchar_t *text = wgettext(k->key.name());
k->button = Environment->addButton(rect, this, k->id, text);
delete[] text;
}
if ((i + 1) % KMaxButtonPerColumns == 0) {
- offset.X += 230 * s;
+ offset.X += 260 * s;
offset.Y = 60 * s;
} else {
offset += v2s32(0, 25 * s);
@@ -430,9 +430,9 @@ void GUIKeyChangeMenu::init_keys()
this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"), "keymap_hotbar_next");
this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom");
this->add_key(GUI_ID_KEY_CAMERA_BUTTON, wgettext("Change camera"), "keymap_camera_mode");
- this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
this->add_key(GUI_ID_KEY_MINIMAP_BUTTON, wgettext("Toggle minimap"), "keymap_minimap");
this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove");
+ this->add_key(GUI_ID_KEY_PITCH_MOVE, wgettext("Toggle pitchmove"), "keymap_pitchmove");
this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove");
this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip");
this->add_key(GUI_ID_KEY_MUTE_BUTTON, wgettext("Mute"), "keymap_mute");
@@ -451,4 +451,3 @@ void GUIKeyChangeMenu::init_keys()
this->add_key(GUI_ID_KEY_CHATLOG_BUTTON, wgettext("Toggle chat log"), "keymap_toggle_chat");
this->add_key(GUI_ID_KEY_FOG_BUTTON, wgettext("Toggle fog"), "keymap_toggle_fog");
}
-
diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp
new file mode 100644
index 000000000..f7218e733
--- /dev/null
+++ b/src/gui/guiScrollBar.cpp
@@ -0,0 +1,425 @@
+/*
+Copyright (C) 2002-2013 Nikolaus Gebhardt
+This file is part of the "Irrlicht Engine".
+For conditions of distribution and use, see copyright notice in irrlicht.h
+
+Modified 2019.05.01 by stujones11, Stuart Jones <stujones111@gmail.com>
+
+This is a heavily modified copy of the Irrlicht CGUIScrollBar class
+which includes automatic scaling of the thumb slider and hiding of
+the arrow buttons where there is insufficient space.
+*/
+
+#include "guiScrollBar.h"
+#include <IGUIButton.h>
+#include <IGUISkin.h>
+
+GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
+ core::rect<s32> rectangle, bool horizontal, bool auto_scale) :
+ IGUIElement(EGUIET_ELEMENT, environment, parent, id, rectangle),
+ up_button(nullptr), down_button(nullptr), is_dragging(false),
+ is_horizontal(horizontal), is_auto_scaling(auto_scale),
+ dragged_by_slider(false), tray_clicked(false), scroll_pos(0),
+ draw_center(0), thumb_size(0), min_pos(0), max_pos(100), small_step(10),
+ large_step(50), last_change(0), drag_offset(0), page_size(100), border_size(0)
+{
+ refreshControls();
+ setNotClipped(false);
+ setTabStop(true);
+ setTabOrder(-1);
+ setPos(0);
+}
+
+bool GUIScrollBar::OnEvent(const SEvent &event)
+{
+ if (isEnabled()) {
+ switch (event.EventType) {
+ case EET_KEY_INPUT_EVENT:
+ if (event.KeyInput.PressedDown) {
+ const s32 old_pos = scroll_pos;
+ bool absorb = true;
+ switch (event.KeyInput.Key) {
+ case KEY_LEFT:
+ case KEY_UP:
+ setPos(scroll_pos - small_step);
+ break;
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ setPos(scroll_pos + small_step);
+ break;
+ case KEY_HOME:
+ setPos(min_pos);
+ break;
+ case KEY_PRIOR:
+ setPos(scroll_pos - large_step);
+ break;
+ case KEY_END:
+ setPos(max_pos);
+ break;
+ case KEY_NEXT:
+ setPos(scroll_pos + large_step);
+ break;
+ default:
+ absorb = false;
+ }
+ if (scroll_pos != old_pos) {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = nullptr;
+ e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
+ Parent->OnEvent(e);
+ }
+ if (absorb)
+ return true;
+ }
+ break;
+ case EET_GUI_EVENT:
+ if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED) {
+ if (event.GUIEvent.Caller == up_button)
+ setPos(scroll_pos - small_step);
+ else if (event.GUIEvent.Caller == down_button)
+ setPos(scroll_pos + small_step);
+
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = nullptr;
+ e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
+ Parent->OnEvent(e);
+ return true;
+ } else if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
+ if (event.GUIEvent.Caller == this)
+ is_dragging = false;
+ break;
+ case EET_MOUSE_INPUT_EVENT: {
+ const core::position2di p(event.MouseInput.X, event.MouseInput.Y);
+ bool is_inside = isPointInside(p);
+ switch (event.MouseInput.Event) {
+ case EMIE_MOUSE_WHEEL:
+ if (Environment->hasFocus(this)) {
+ s8 d = event.MouseInput.Wheel < 0 ? -1 : 1;
+ s8 h = is_horizontal ? 1 : -1;
+ setPos(getPos() + (d * small_step * h));
+
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = nullptr;
+ e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
+ Parent->OnEvent(e);
+ return true;
+ }
+ break;
+ case EMIE_LMOUSE_PRESSED_DOWN: {
+ if (is_inside) {
+ is_dragging = true;
+ dragged_by_slider = slider_rect.isPointInside(p);
+ core::vector2di corner = slider_rect.UpperLeftCorner;
+ drag_offset = is_horizontal ? p.X - corner.X : p.Y - corner.Y;
+ tray_clicked = !dragged_by_slider;
+ if (tray_clicked) {
+ const s32 new_pos = getPosFromMousePos(p);
+ const s32 old_pos = scroll_pos;
+ setPos(new_pos);
+ // drag in the middle
+ drag_offset = thumb_size / 2;
+ // report the scroll event
+ if (scroll_pos != old_pos && Parent) {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = nullptr;
+ e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
+ Parent->OnEvent(e);
+ }
+ }
+ Environment->setFocus(this);
+ return true;
+ }
+ break;
+ }
+ case EMIE_LMOUSE_LEFT_UP:
+ case EMIE_MOUSE_MOVED: {
+ if (!event.MouseInput.isLeftPressed())
+ is_dragging = false;
+
+ if (!is_dragging) {
+ if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
+ break;
+ return is_inside;
+ }
+
+ if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
+ is_dragging = false;
+
+ // clang-format off
+ if (!dragged_by_slider) {
+ if (is_inside) {
+ dragged_by_slider = slider_rect.isPointInside(p);
+ tray_clicked = !dragged_by_slider;
+ }
+ if (!dragged_by_slider) {
+ tray_clicked = false;
+ if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
+ return is_inside;
+ }
+ }
+ // clang-format on
+
+ const s32 new_pos = getPosFromMousePos(p);
+ const s32 old_pos = scroll_pos;
+
+ setPos(new_pos);
+
+ if (scroll_pos != old_pos && Parent) {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = nullptr;
+ e.GUIEvent.EventType = EGET_SCROLL_BAR_CHANGED;
+ Parent->OnEvent(e);
+ }
+ return is_inside;
+ }
+ default:
+ break;
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+ return IGUIElement::OnEvent(event);
+}
+
+void GUIScrollBar::draw()
+{
+ if (!IsVisible)
+ return;
+
+ IGUISkin *skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ video::SColor icon_color = skin->getColor(
+ isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
+ if (icon_color != current_icon_color)
+ refreshControls();
+
+ slider_rect = AbsoluteRect;
+ skin->draw2DRectangle(this, skin->getColor(EGDC_SCROLLBAR), slider_rect,
+ &AbsoluteClippingRect);
+
+ if (core::isnotzero(range())) {
+ if (is_horizontal) {
+ slider_rect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X +
+ draw_center - thumb_size / 2;
+ slider_rect.LowerRightCorner.X =
+ slider_rect.UpperLeftCorner.X + thumb_size;
+ } else {
+ slider_rect.UpperLeftCorner.Y = AbsoluteRect.UpperLeftCorner.Y +
+ draw_center - thumb_size / 2;
+ slider_rect.LowerRightCorner.Y =
+ slider_rect.UpperLeftCorner.Y + thumb_size;
+ }
+ skin->draw3DButtonPaneStandard(this, slider_rect, &AbsoluteClippingRect);
+ }
+ IGUIElement::draw();
+}
+
+void GUIScrollBar::updateAbsolutePosition()
+{
+ IGUIElement::updateAbsolutePosition();
+ refreshControls();
+ setPos(scroll_pos);
+}
+
+s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
+{
+ s32 w, p;
+ s32 offset = dragged_by_slider ? drag_offset : thumb_size / 2;
+
+ if (is_horizontal) {
+ w = RelativeRect.getWidth() - border_size * 2 - thumb_size;
+ p = pos.X - AbsoluteRect.UpperLeftCorner.X - border_size - offset;
+ } else {
+ w = RelativeRect.getHeight() - border_size * 2 - thumb_size;
+ p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset;
+ }
+ return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range()) + min_pos : 0;
+}
+
+void GUIScrollBar::setPos(const s32 &pos)
+{
+ s32 thumb_area = 0;
+ s32 thumb_min = 0;
+
+ if (is_horizontal) {
+ thumb_min = RelativeRect.getHeight();
+ thumb_area = RelativeRect.getWidth() - border_size * 2;
+ } else {
+ thumb_min = RelativeRect.getWidth();
+ thumb_area = RelativeRect.getHeight() - border_size * 2;
+ }
+
+ if (is_auto_scaling)
+ thumb_size = s32(thumb_area /
+ (f32(page_size) / f32(thumb_area + border_size * 2)));
+
+ thumb_size = core::s32_clamp(thumb_size, thumb_min, thumb_area);
+ scroll_pos = core::s32_clamp(pos, min_pos, max_pos);
+
+ f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range()
+ : 1.0f;
+ draw_center = s32((f32(scroll_pos) * f) + (f32(thumb_size) * 0.5f)) + border_size;
+}
+
+void GUIScrollBar::setSmallStep(const s32 &step)
+{
+ small_step = step > 0 ? step : 10;
+}
+
+void GUIScrollBar::setLargeStep(const s32 &step)
+{
+ large_step = step > 0 ? step : 50;
+}
+
+void GUIScrollBar::setMax(const s32 &max)
+{
+ max_pos = max;
+ if (min_pos > max_pos)
+ min_pos = max_pos;
+
+ bool enable = core::isnotzero(range());
+ up_button->setEnabled(enable);
+ down_button->setEnabled(enable);
+ setPos(scroll_pos);
+}
+
+void GUIScrollBar::setMin(const s32 &min)
+{
+ min_pos = min;
+ if (max_pos < min_pos)
+ max_pos = min_pos;
+
+ bool enable = core::isnotzero(range());
+ up_button->setEnabled(enable);
+ down_button->setEnabled(enable);
+ setPos(scroll_pos);
+}
+
+void GUIScrollBar::setPageSize(const s32 &size)
+{
+ page_size = size;
+ setPos(scroll_pos);
+}
+
+s32 GUIScrollBar::getPos() const
+{
+ return scroll_pos;
+}
+
+void GUIScrollBar::refreshControls()
+{
+ IGUISkin *skin = Environment->getSkin();
+ IGUISpriteBank *sprites = nullptr;
+ current_icon_color = video::SColor(255, 255, 255, 255);
+
+ if (skin) {
+ sprites = skin->getSpriteBank();
+ current_icon_color =
+ skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL
+ : EGDC_GRAY_WINDOW_SYMBOL);
+ }
+ if (is_horizontal) {
+ s32 h = RelativeRect.getHeight();
+ border_size = RelativeRect.getWidth() < h * 4 ? 0 : h;
+ if (!up_button) {
+ up_button = Environment->addButton(
+ core::rect<s32>(0, 0, h, h), this);
+ up_button->setSubElement(true);
+ up_button->setTabStop(false);
+ }
+ if (sprites) {
+ up_button->setSpriteBank(sprites);
+ up_button->setSprite(EGBS_BUTTON_UP,
+ s32(skin->getIcon(EGDI_CURSOR_LEFT)),
+ current_icon_color);
+ up_button->setSprite(EGBS_BUTTON_DOWN,
+ s32(skin->getIcon(EGDI_CURSOR_LEFT)),
+ current_icon_color);
+ }
+ up_button->setRelativePosition(core::rect<s32>(0, 0, h, h));
+ up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT,
+ EGUIA_LOWERRIGHT);
+ if (!down_button) {
+ down_button = Environment->addButton(
+ core::rect<s32>(RelativeRect.getWidth() - h, 0,
+ RelativeRect.getWidth(), h),
+ this);
+ down_button->setSubElement(true);
+ down_button->setTabStop(false);
+ }
+ if (sprites) {
+ down_button->setSpriteBank(sprites);
+ down_button->setSprite(EGBS_BUTTON_UP,
+ s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
+ current_icon_color);
+ down_button->setSprite(EGBS_BUTTON_DOWN,
+ s32(skin->getIcon(EGDI_CURSOR_RIGHT)),
+ current_icon_color);
+ }
+ down_button->setRelativePosition(
+ core::rect<s32>(RelativeRect.getWidth() - h, 0,
+ RelativeRect.getWidth(), h));
+ down_button->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT,
+ EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
+ } else {
+ s32 w = RelativeRect.getWidth();
+ border_size = RelativeRect.getHeight() < w * 4 ? 0 : w;
+ if (!up_button) {
+ up_button = Environment->addButton(
+ core::rect<s32>(0, 0, w, w), this);
+ up_button->setSubElement(true);
+ up_button->setTabStop(false);
+ }
+ if (sprites) {
+ up_button->setSpriteBank(sprites);
+ up_button->setSprite(EGBS_BUTTON_UP,
+ s32(skin->getIcon(EGDI_CURSOR_UP)),
+ current_icon_color);
+ up_button->setSprite(EGBS_BUTTON_DOWN,
+ s32(skin->getIcon(EGDI_CURSOR_UP)),
+ current_icon_color);
+ }
+ up_button->setRelativePosition(core::rect<s32>(0, 0, w, w));
+ up_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
+ EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
+ if (!down_button) {
+ down_button = Environment->addButton(
+ core::rect<s32>(0, RelativeRect.getHeight() - w,
+ w, RelativeRect.getHeight()),
+ this);
+ down_button->setSubElement(true);
+ down_button->setTabStop(false);
+ }
+ if (sprites) {
+ down_button->setSpriteBank(sprites);
+ down_button->setSprite(EGBS_BUTTON_UP,
+ s32(skin->getIcon(EGDI_CURSOR_DOWN)),
+ current_icon_color);
+ down_button->setSprite(EGBS_BUTTON_DOWN,
+ s32(skin->getIcon(EGDI_CURSOR_DOWN)),
+ current_icon_color);
+ }
+ down_button->setRelativePosition(
+ core::rect<s32>(0, RelativeRect.getHeight() - w, w,
+ RelativeRect.getHeight()));
+ down_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
+ EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
+ }
+ bool visible = (border_size != 0);
+ up_button->setVisible(visible);
+ down_button->setVisible(visible);
+}
diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h
new file mode 100644
index 000000000..349411fc1
--- /dev/null
+++ b/src/gui/guiScrollBar.h
@@ -0,0 +1,69 @@
+/*
+Copyright (C) 2002-2013 Nikolaus Gebhardt
+This file is part of the "Irrlicht Engine".
+For conditions of distribution and use, see copyright notice in irrlicht.h
+
+Modified 2019.05.01 by stujones11, Stuart Jones <stujones111@gmail.com>
+
+This is a heavily modified copy of the Irrlicht CGUIScrollBar class
+which includes automatic scaling of the thumb slider and hiding of
+the arrow buttons where there is insufficient space.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+
+using namespace irr;
+using namespace gui;
+
+class GUIScrollBar : public IGUIElement
+{
+public:
+ GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
+ core::rect<s32> rectangle, bool horizontal, bool auto_scale);
+
+ virtual void draw();
+ virtual void updateAbsolutePosition();
+ virtual bool OnEvent(const SEvent &event);
+
+ s32 getMax() const { return max_pos; }
+ s32 getMin() const { return min_pos; }
+ s32 getLargeStep() const { return large_step; }
+ s32 getSmallStep() const { return small_step; }
+ s32 getPos() const;
+
+ void setMax(const s32 &max);
+ void setMin(const s32 &min);
+ void setSmallStep(const s32 &step);
+ void setLargeStep(const s32 &step);
+ void setPos(const s32 &pos);
+ void setPageSize(const s32 &size);
+
+private:
+ void refreshControls();
+ s32 getPosFromMousePos(const core::position2di &p) const;
+ f32 range() const { return f32(max_pos - min_pos); }
+
+ IGUIButton *up_button;
+ IGUIButton *down_button;
+ bool is_dragging;
+ bool is_horizontal;
+ bool is_auto_scaling;
+ bool dragged_by_slider;
+ bool tray_clicked;
+ s32 scroll_pos;
+ s32 draw_center;
+ s32 thumb_size;
+ s32 min_pos;
+ s32 max_pos;
+ s32 small_step;
+ s32 large_step;
+ u32 last_change;
+ s32 drag_offset;
+ s32 page_size;
+ s32 border_size;
+
+ core::rect<s32> slider_rect;
+ video::SColor current_icon_color;
+};
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; i<EGDF_COUNT; ++i)
+ Fonts[i] = 0;
+
+ UseGradient = (Type == EGST_WINDOWS_METALLIC) || (Type == EGST_BURNING_SKIN) ;
+}
+
+
+//! destructor
+GUISkin::~GUISkin()
+{
+ for (u32 i=0; i<EGDF_COUNT; ++i)
+ {
+ if (Fonts[i])
+ Fonts[i]->drop();
+ }
+
+ 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<s32>& r,
+ const core::rect<s32>* clip,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& r,
+ const core::rect<s32>* clip,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& r,
+ const core::rect<s32>* clip,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32> GUISkin::drawColored3DWindowBackground(IGUIElement* element,
+ bool drawTitleBar, video::SColor titleBarColor,
+ const core::rect<s32>& r,
+ const core::rect<s32>* clip,
+ core::rect<s32>* checkClientArea,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ {
+ if ( checkClientArea )
+ {
+ *checkClientArea = r;
+ }
+ return r;
+ }
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& r, const core::rect<s32>* clip,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& r,
+ const core::rect<s32>* clip,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& frameRect, const core::rect<s32>* clip, EGUI_ALIGNMENT alignment,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>& rect, const core::rect<s32>* clip, s32 tabHeight, EGUI_ALIGNMENT alignment,
+ const video::SColor* colors)
+{
+ if (!Driver)
+ return;
+
+ if (!colors)
+ colors = Colors;
+
+ core::rect<s32> 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<s32>* 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<s32>& pos,
+ const core::rect<s32>* 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; i<EGDC_COUNT; ++i)
+ out->addColor(GUISkinColorNames[i], Colors[i]);
+
+ for (i=0; i<EGDS_COUNT; ++i)
+ out->addInt(GUISkinSizeNames[i], Sizes[i]);
+
+ for (i=0; i<EGDT_COUNT; ++i)
+ out->addString(GUISkinTextNames[i], Texts[i].c_str());
+
+ for (i=0; i<EGDI_COUNT; ++i)
+ out->addInt(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; i<EGDC_COUNT; ++i)
+ Colors[i] = in->getAttributeAsColor(GUISkinColorNames[i]);
+
+ for (i=0; i<EGDS_COUNT; ++i)
+ Sizes[i] = in->getAttributeAsInt(GUISkinSizeNames[i]);
+
+ for (i=0; i<EGDT_COUNT; ++i)
+ Texts[i] = in->getAttributeAsStringW(GUISkinTextNames[i]);
+
+ for (i=0; i<EGDI_COUNT; ++i)
+ Icons[i] = in->getAttributeAsInt(GUISkinIconNames[i]);
+}
+
+
+//! gets the colors
+// PATCH
+void GUISkin::getColors(video::SColor* colors)
+{
+ u32 i;
+ for (i=0; i<EGDC_COUNT; ++i)
+ colors[i] = Colors[i];
+}
+// END PATCH
+
+} // end namespace gui
+} // end namespace irr
+
+
+#endif // _IRR_COMPILE_WITH_GUI_
+
diff --git a/src/gui/guiSkin.h b/src/gui/guiSkin.h
new file mode 100644
index 000000000..bbb900f9f
--- /dev/null
+++ b/src/gui/guiSkin.h
@@ -0,0 +1,376 @@
+// 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
+
+#ifndef __GUI_SKIN_H_INCLUDED__
+#define __GUI_SKIN_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUISkin.h"
+#include "irrString.h"
+#include <string>
+#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<s32>& rect,
+ const core::rect<s32>* clip=0)
+ {
+ drawColored3DButtonPaneStandard(element, rect,clip);
+ }
+
+ virtual void drawColored3DButtonPaneStandard(IGUIElement* element,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* 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<s32>& rect,
+ const core::rect<s32>* clip=0)
+ {
+ drawColored3DButtonPanePressed(element, rect, clip);
+ }
+
+ virtual void drawColored3DButtonPanePressed(IGUIElement* element,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* 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<s32>& rect,
+ const core::rect<s32>* clip=0)
+ {
+ drawColored3DSunkenPane(element, bgcolor, flat, fillBackGround, rect, clip);
+ }
+
+ virtual void drawColored3DSunkenPane(IGUIElement* element,
+ video::SColor bgcolor, bool flat,
+ bool fillBackGround,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* 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<s32> draw3DWindowBackground(IGUIElement* element,
+ bool drawTitleBar, video::SColor titleBarColor,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* clip,
+ core::rect<s32>* checkClientArea)
+ {
+ return drawColored3DWindowBackground(element, drawTitleBar, titleBarColor,
+ rect, clip, checkClientArea);
+ }
+
+ virtual core::rect<s32> drawColored3DWindowBackground(IGUIElement* element,
+ bool drawTitleBar, video::SColor titleBarColor,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* clip,
+ core::rect<s32>* 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<s32>& rect,
+ const core::rect<s32>* clip=0)
+ {
+ drawColored3DMenuPane(element, rect, clip);
+ }
+
+ virtual void drawColored3DMenuPane(IGUIElement* element,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* 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<s32>& rect,
+ const core::rect<s32>* clip=0)
+ {
+ drawColored3DToolBar(element, rect, clip);
+ }
+
+ virtual void drawColored3DToolBar(IGUIElement* element,
+ const core::rect<s32>& rect,
+ const core::rect<s32>* 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<s32>& rect, const core::rect<s32>* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT)
+ {
+ drawColored3DTabButton(element, active, rect, clip, alignment);
+ }
+
+ virtual void drawColored3DTabButton(IGUIElement* element, bool active,
+ const core::rect<s32>& rect, const core::rect<s32>* 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<s32>& rect, const core::rect<s32>* 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<s32>& rect, const core::rect<s32>* 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<s32>* 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<s32>* 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<s32>& pos, const core::rect<s32>* 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/src/gui/guiTable.cpp b/src/gui/guiTable.cpp
index a123bdd6d..c705e17fb 100644
--- a/src/gui/guiTable.cpp
+++ b/src/gui/guiTable.cpp
@@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cstring>
#include <IGUISkin.h>
#include <IGUIFont.h>
-#include <IGUIScrollBar.h>
#include "client/renderingengine.h"
#include "debug.h"
#include "log.h"
@@ -62,12 +61,12 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
}
const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
- m_scrollbar = Environment->addScrollBar(false,
+ m_scrollbar = new GUIScrollBar(Environment, this, -1,
core::rect<s32>(RelativeRect.getWidth() - s,
0,
RelativeRect.getWidth(),
RelativeRect.getHeight()),
- this, -1);
+ false, true);
m_scrollbar->setSubElement(true);
m_scrollbar->setTabStop(false);
m_scrollbar->setAlignment(gui::EGUIA_LOWERRIGHT, gui::EGUIA_LOWERRIGHT,
@@ -99,7 +98,8 @@ GUITable::~GUITable()
if (m_font)
m_font->drop();
- m_scrollbar->remove();
+ if (m_scrollbar)
+ m_scrollbar->drop();
}
GUITable::Option GUITable::splitOption(const std::string &str)
@@ -1075,6 +1075,7 @@ void GUITable::updateScrollBar()
m_scrollbar->setMax(scrollmax);
m_scrollbar->setSmallStep(m_rowheight);
m_scrollbar->setLargeStep(2 * m_rowheight);
+ m_scrollbar->setPageSize(totalheight);
}
void GUITable::sendTableEvent(s32 column, bool doubleclick)
diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h
index f9337ff6d..11093ea72 100644
--- a/src/gui/guiTable.h
+++ b/src/gui/guiTable.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include "irrlichttypes_extrabloated.h"
+#include "guiScrollBar.h"
class ISimpleTextureSource;
@@ -198,7 +199,7 @@ protected:
video::SColor m_highlight_text = video::SColor(255, 255, 255, 255);
s32 m_rowheight = 1;
gui::IGUIFont *m_font = nullptr;
- gui::IGUIScrollBar *m_scrollbar = nullptr;
+ GUIScrollBar *m_scrollbar = nullptr;
// Allocated strings and images
std::vector<core::stringw> m_strings;
diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp
index e0cb6fa72..45d2ee139 100644
--- a/src/gui/guiVolumeChange.cpp
+++ b/src/gui/guiVolumeChange.cpp
@@ -58,6 +58,9 @@ void GUIVolumeChange::removeChildren()
if (gui::IGUIElement *e = getElementFromId(ID_soundSlider))
e->remove();
+
+ if (gui::IGUIElement *e = getElementFromId(ID_soundMuteButton))
+ e->remove();
}
void GUIVolumeChange::regenerateGui(v2u32 screensize)
@@ -193,4 +196,3 @@ bool GUIVolumeChange::OnEvent(const SEvent& event)
return Parent ? Parent->OnEvent(event) : false;
}
-
diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
index fd3caa0f7..10395423c 100644
--- a/src/gui/intlGUIEditBox.cpp
+++ b/src/gui/intlGUIEditBox.cpp
@@ -113,6 +113,9 @@ intlGUIEditBox::~intlGUIEditBox()
if (Operator)
Operator->drop();
+
+ if (m_vscrollbar)
+ m_vscrollbar->drop();
}
@@ -1479,7 +1482,9 @@ void intlGUIEditBox::createVScrollBar()
irr::core::rect<s32> scrollbarrect = FrameRect;
scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
- m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
+ m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
+ scrollbarrect, false, true);
+
m_vscrollbar->setVisible(false);
m_vscrollbar->setSmallStep(3 * fontHeight);
m_vscrollbar->setLargeStep(10 * fontHeight);
@@ -1501,6 +1506,7 @@ void intlGUIEditBox::updateVScrollBar()
if (scrollymax != m_vscrollbar->getMax()) {
// manage a newline or a deleted line
m_vscrollbar->setMax(scrollymax);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
calculateScrollPos();
} else {
// manage a newline or a deleted line
@@ -1513,6 +1519,7 @@ void intlGUIEditBox::updateVScrollBar()
s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
if (scrollymax != m_vscrollbar->getMax()) {
m_vscrollbar->setMax(scrollymax);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
}
if (!m_vscrollbar->isVisible() && MultiLine) {
@@ -1527,6 +1534,7 @@ void intlGUIEditBox::updateVScrollBar()
VScrollPos = 0;
m_vscrollbar->setPos(0);
m_vscrollbar->setMax(1);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
m_vscrollbar->setVisible(false);
}
}
diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h
index 3aa4f00b4..9d643495e 100644
--- a/src/gui/intlGUIEditBox.h
+++ b/src/gui/intlGUIEditBox.h
@@ -10,7 +10,7 @@
#include <IGUIEditBox.h>
#include "irrArray.h"
#include "IOSOperator.h"
-#include "IGUIScrollBar.h"
+#include "guiScrollBar.h"
namespace irr
{
@@ -198,7 +198,7 @@ namespace gui
core::rect<s32> CurrentTextRect = core::rect<s32>(0,0,1,1);
core::rect<s32> FrameRect; // temporary values
u32 m_scrollbar_width;
- IGUIScrollBar *m_vscrollbar;
+ GUIScrollBar *m_vscrollbar;
bool m_writable;
};
diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h
index ea9327813..102492255 100644
--- a/src/gui/mainmenumanager.h
+++ b/src/gui/mainmenumanager.h
@@ -62,21 +62,7 @@ public:
virtual void deletingMenu(gui::IGUIElement *menu)
{
// Remove all entries if there are duplicates
- bool removed_entry;
- do{
- removed_entry = false;
- for(std::list<gui::IGUIElement*>::iterator
- i = m_stack.begin();
- i != m_stack.end(); ++i)
- {
- if(*i == menu)
- {
- m_stack.erase(i);
- removed_entry = true;
- break;
- }
- }
- }while(removed_entry);
+ m_stack.remove(menu);
/*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
assert(*i == menu);
diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp
index 4ffe88800..30417943d 100644
--- a/src/gui/modalMenu.cpp
+++ b/src/gui/modalMenu.cpp
@@ -260,7 +260,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
return retval;
}
}
- // clang-format on
+ // clang-format on
#endif
return false;
}
diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h
index 421f70963..1f9adda22 100644
--- a/src/gui/touchscreengui.h
+++ b/src/gui/touchscreengui.h
@@ -33,7 +33,8 @@ using namespace irr;
using namespace irr::core;
using namespace irr::gui;
-typedef enum {
+typedef enum
+{
jump_id = 0,
crunch_id,
zoom_id,
@@ -61,7 +62,8 @@ typedef enum {
joystick_center_id
} touch_gui_button_id;
-typedef enum {
+typedef enum
+{
j_forward = 0,
j_backward,
j_left,
@@ -69,7 +71,8 @@ typedef enum {
j_special1
} touch_gui_joystick_move_id;
-typedef enum {
+typedef enum
+{
AHBB_Dir_Top_Bottom,
AHBB_Dir_Bottom_Top,
AHBB_Dir_Left_Right,