diff options
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/gui/StyleSpec.h | 135 | ||||
-rw-r--r-- | src/gui/guiButton.cpp | 649 | ||||
-rw-r--r-- | src/gui/guiButton.h | 310 | ||||
-rw-r--r-- | src/gui/guiChatConsole.cpp | 18 | ||||
-rw-r--r-- | src/gui/guiChatConsole.h | 2 | ||||
-rw-r--r-- | src/gui/guiConfirmRegistration.cpp | 9 | ||||
-rw-r--r-- | src/gui/guiConfirmRegistration.h | 3 | ||||
-rw-r--r-- | src/gui/guiEditBoxWithScrollbar.cpp | 12 | ||||
-rw-r--r-- | src/gui/guiEditBoxWithScrollbar.h | 6 | ||||
-rw-r--r-- | src/gui/guiEngine.cpp | 19 | ||||
-rw-r--r-- | src/gui/guiEngine.h | 6 | ||||
-rw-r--r-- | src/gui/guiFormSpecMenu.cpp | 864 | ||||
-rw-r--r-- | src/gui/guiFormSpecMenu.h | 46 | ||||
-rw-r--r-- | src/gui/guiKeyChangeMenu.cpp | 13 | ||||
-rw-r--r-- | src/gui/guiScrollBar.cpp | 425 | ||||
-rw-r--r-- | src/gui/guiScrollBar.h | 69 | ||||
-rw-r--r-- | src/gui/guiSkin.cpp | 1084 | ||||
-rw-r--r-- | src/gui/guiSkin.h | 376 | ||||
-rw-r--r-- | src/gui/guiTable.cpp | 9 | ||||
-rw-r--r-- | src/gui/guiTable.h | 3 | ||||
-rw-r--r-- | src/gui/guiVolumeChange.cpp | 4 | ||||
-rw-r--r-- | src/gui/intlGUIEditBox.cpp | 10 | ||||
-rw-r--r-- | src/gui/intlGUIEditBox.h | 4 | ||||
-rw-r--r-- | src/gui/mainmenumanager.h | 16 | ||||
-rw-r--r-- | src/gui/modalMenu.cpp | 2 | ||||
-rw-r--r-- | src/gui/touchscreengui.h | 9 |
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, |