2019-03-15 19:39:23 +01:00
|
|
|
// 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"
|
|
|
|
|
|
|
|
|
2019-12-09 21:06:51 +01:00
|
|
|
#include "client/guiscalingfilter.h"
|
|
|
|
#include "client/tile.h"
|
2019-03-15 19:39:23 +01:00
|
|
|
#include "IGUISkin.h"
|
|
|
|
#include "IGUIEnvironment.h"
|
|
|
|
#include "IVideoDriver.h"
|
|
|
|
#include "IGUIFont.h"
|
2019-12-09 21:06:51 +01:00
|
|
|
#include "irrlicht_changes/static_text.h"
|
2019-03-15 19:39:23 +01:00
|
|
|
#include "porting.h"
|
2019-12-09 21:06:51 +01:00
|
|
|
#include "StyleSpec.h"
|
2020-04-11 22:39:30 +02:00
|
|
|
#include "util/numeric.h"
|
2019-03-15 19:39:23 +01:00
|
|
|
|
|
|
|
using namespace irr;
|
|
|
|
using namespace gui;
|
|
|
|
|
2019-10-12 18:44:23 +02:00
|
|
|
// Multiply with a color to get the default corresponding hovered color
|
|
|
|
#define COLOR_HOVERED_MOD 1.25f
|
|
|
|
|
|
|
|
// Multiply with a color to get the default corresponding pressed color
|
|
|
|
#define COLOR_PRESSED_MOD 0.85f
|
|
|
|
|
2019-03-15 19:39:23 +01:00
|
|
|
//! constructor
|
|
|
|
GUIButton::GUIButton(IGUIEnvironment* environment, IGUIElement* parent,
|
2020-04-11 22:39:30 +02:00
|
|
|
s32 id, core::rect<s32> rectangle, ISimpleTextureSource *tsrc,
|
|
|
|
bool noclip)
|
2019-03-15 19:39:23 +01:00
|
|
|
: 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),
|
2020-04-11 22:39:30 +02:00
|
|
|
UseAlphaChannel(false), DrawBorder(true), ScaleImage(false), TSrc(tsrc)
|
2019-03-15 19:39:23 +01:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2019-12-09 21:06:51 +01:00
|
|
|
StaticText = gui::StaticText::add(Environment, Text.c_str(), core::rect<s32>(0,0,rectangle.getWidth(),rectangle.getHeight()), false, false, this, id);
|
|
|
|
StaticText->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER);
|
2019-03-15 19:39:23 +01:00
|
|
|
// 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)
|
|
|
|
{
|
2020-01-22 10:18:09 +01:00
|
|
|
// Sometimes formspec elements can receive mouse events when the
|
|
|
|
// mouse is outside of the formspec. Thus, we test the position here.
|
|
|
|
if ( !IsPushButton && AbsoluteClippingRect.isPointInside(
|
|
|
|
core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y ))) {
|
2019-03-15 19:39:23 +01:00
|
|
|
setPressed(true);
|
2020-01-22 10:18:09 +01:00
|
|
|
}
|
2019-03-15 19:39:23 +01:00
|
|
|
|
|
|
|
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
|
2020-04-11 22:39:30 +02:00
|
|
|
// Track hovered state, if it has changed then we need to update the style.
|
|
|
|
bool hovered = isHovered();
|
|
|
|
if (hovered != WasHovered) {
|
|
|
|
WasHovered = hovered;
|
|
|
|
setFromState();
|
|
|
|
}
|
|
|
|
|
2019-03-15 19:39:23 +01:00
|
|
|
GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
|
|
|
|
video::IVideoDriver* driver = Environment->getVideoDriver();
|
|
|
|
// END PATCH
|
|
|
|
|
|
|
|
if (DrawBorder)
|
|
|
|
{
|
|
|
|
if (!Pressed)
|
|
|
|
{
|
|
|
|
// PATCH
|
2020-04-11 22:39:30 +02:00
|
|
|
skin->drawColored3DButtonPaneStandard(this, AbsoluteRect,
|
|
|
|
&AbsoluteClippingRect, Colors);
|
2019-03-15 19:39:23 +01:00
|
|
|
// END PATCH
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// PATCH
|
2020-04-11 22:39:30 +02:00
|
|
|
skin->drawColored3DButtonPanePressed(this, AbsoluteRect,
|
|
|
|
&AbsoluteClippingRect, Colors);
|
2019-03-15 19:39:23 +01:00
|
|
|
// END PATCH
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const core::position2di buttonCenter(AbsoluteRect.getCenter());
|
2020-04-11 22:39:30 +02:00
|
|
|
// PATCH
|
|
|
|
// The image changes based on the state, so we use the default every time.
|
|
|
|
EGUI_BUTTON_IMAGE_STATE imageState = EGBIS_IMAGE_UP;
|
|
|
|
// END PATCH
|
2019-03-15 19:39:23 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-26 20:35:26 +01:00
|
|
|
// PATCH
|
|
|
|
video::ITexture* texture = ButtonImages[(u32)imageState].Texture;
|
2020-08-29 17:41:19 +02:00
|
|
|
video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor };
|
2020-01-26 20:35:26 +01:00
|
|
|
if (BgMiddle.getArea() == 0) {
|
|
|
|
driver->draw2DImage(texture,
|
|
|
|
ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
|
|
|
|
sourceRect, &AbsoluteClippingRect,
|
2020-08-29 17:41:19 +02:00
|
|
|
image_colors, UseAlphaChannel);
|
2020-01-26 20:35:26 +01:00
|
|
|
} else {
|
|
|
|
core::rect<s32> middle = BgMiddle;
|
|
|
|
// `-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,
|
|
|
|
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
|
2020-08-29 17:41:19 +02:00
|
|
|
middle, &AbsoluteClippingRect, image_colors);
|
2020-01-26 20:35:26 +01:00
|
|
|
}
|
|
|
|
// END PATCH
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-12-09 21:06:51 +01:00
|
|
|
state = isHovered() ? EGBS_BUTTON_MOUSE_OVER : EGBS_BUTTON_MOUSE_OFF;
|
2019-03-15 19:39:23 +01:00
|
|
|
drawSprite(state, HoverTime, pos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// draw disabled
|
|
|
|
// drawSprite(EGBS_BUTTON_DISABLED, 0, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-12-09 21:06:51 +01:00
|
|
|
{
|
|
|
|
// PATCH
|
|
|
|
return getImageState(pressed, ButtonImages);
|
|
|
|
// END PATCH
|
|
|
|
}
|
|
|
|
|
|
|
|
EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed, const ButtonImage* images) const
|
2019-03-15 19:39:23 +01:00
|
|
|
{
|
|
|
|
// figure state we should have
|
|
|
|
EGUI_BUTTON_IMAGE_STATE state = EGBIS_IMAGE_DISABLED;
|
|
|
|
bool focused = Environment->hasFocus((IGUIElement*)this);
|
2019-12-09 21:06:51 +01:00
|
|
|
bool mouseOver = isHovered();
|
2019-03-15 19:39:23 +01:00
|
|
|
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
|
2019-12-09 21:06:51 +01:00
|
|
|
while ( state != EGBIS_IMAGE_UP && !images[(u32)state].Texture )
|
2019-03-15 19:39:23 +01:00
|
|
|
{
|
2019-10-12 18:44:23 +02:00
|
|
|
// PATCH
|
2019-03-15 19:39:23 +01:00
|
|
|
switch ( state )
|
|
|
|
{
|
|
|
|
case EGBIS_IMAGE_UP_FOCUSED:
|
2019-10-12 18:44:23 +02:00
|
|
|
state = EGBIS_IMAGE_UP;
|
2019-03-15 19:39:23 +01:00
|
|
|
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:
|
2019-10-12 18:44:23 +02:00
|
|
|
state = EGBIS_IMAGE_DOWN;
|
2019-03-15 19:39:23 +01:00
|
|
|
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;
|
|
|
|
}
|
2019-10-12 18:44:23 +02:00
|
|
|
// END PATCH
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2019-12-09 21:06:51 +01:00
|
|
|
|
|
|
|
StaticText->setOverrideFont(font);
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//! 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;
|
2019-12-09 21:06:51 +01:00
|
|
|
|
|
|
|
StaticText->setOverrideColor(color);
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-12 18:44:23 +02:00
|
|
|
// PATCH
|
|
|
|
void GUIButton::setImage(video::ITexture* image)
|
|
|
|
{
|
|
|
|
setImage(gui::EGBIS_IMAGE_UP, image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIButton::setImage(video::ITexture* image, const core::rect<s32>& pos)
|
|
|
|
{
|
|
|
|
setImage(gui::EGBIS_IMAGE_UP, image, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIButton::setPressedImage(video::ITexture* image)
|
|
|
|
{
|
|
|
|
setImage(gui::EGBIS_IMAGE_DOWN, image);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIButton::setPressedImage(video::ITexture* image, const core::rect<s32>& pos)
|
|
|
|
{
|
|
|
|
setImage(gui::EGBIS_IMAGE_DOWN, image, pos);
|
|
|
|
}
|
|
|
|
|
2019-12-09 21:06:51 +01:00
|
|
|
//! Sets the text displayed by the button
|
|
|
|
void GUIButton::setText(const wchar_t* text)
|
|
|
|
{
|
|
|
|
StaticText->setText(text);
|
|
|
|
|
|
|
|
IGUIButton::setText(text);
|
|
|
|
}
|
2019-10-12 18:44:23 +02:00
|
|
|
// END PATCH
|
|
|
|
|
2019-03-15 19:39:23 +01:00
|
|
|
//! 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;
|
|
|
|
}
|
|
|
|
|
2019-12-09 21:06:51 +01:00
|
|
|
// PATCH
|
|
|
|
//! Returns if this element (or one of its direct children) is hovered
|
|
|
|
bool GUIButton::isHovered() const
|
|
|
|
{
|
|
|
|
IGUIElement *hovered = Environment->getHovered();
|
|
|
|
return hovered == this || (hovered != nullptr && hovered->getParent() == this);
|
|
|
|
}
|
|
|
|
// END PATCH
|
2019-03-15 19:39:23 +01:00
|
|
|
|
|
|
|
//! 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;
|
2020-04-11 22:39:30 +02:00
|
|
|
setFromState();
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! 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
|
2020-04-11 22:39:30 +02:00
|
|
|
GUIButton* GUIButton::addButton(IGUIEnvironment *environment,
|
|
|
|
const core::rect<s32>& rectangle, ISimpleTextureSource *tsrc,
|
|
|
|
IGUIElement* parent, s32 id, const wchar_t* text,
|
|
|
|
const wchar_t *tooltiptext)
|
2019-03-15 19:39:23 +01:00
|
|
|
{
|
2020-04-11 22:39:30 +02:00
|
|
|
GUIButton* button = new GUIButton(environment, parent ? parent : environment->getRootGUIElement(), id, rectangle, tsrc);
|
2019-03-15 19:39:23 +01:00
|
|
|
if (text)
|
|
|
|
button->setText(text);
|
|
|
|
|
|
|
|
if ( tooltiptext )
|
|
|
|
button->setToolTipText ( tooltiptext );
|
|
|
|
|
|
|
|
button->drop();
|
|
|
|
return button;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GUIButton::setColor(video::SColor color)
|
|
|
|
{
|
2020-08-29 17:41:19 +02:00
|
|
|
BgColor = color;
|
|
|
|
|
2019-03-15 19:39:23 +01:00
|
|
|
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);
|
2019-10-12 18:44:23 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-11 22:39:30 +02:00
|
|
|
|
|
|
|
//! Set element properties from a StyleSpec corresponding to the button state
|
|
|
|
void GUIButton::setFromState()
|
2019-10-12 18:44:23 +02:00
|
|
|
{
|
2020-04-11 22:39:30 +02:00
|
|
|
StyleSpec::State state = StyleSpec::STATE_DEFAULT;
|
|
|
|
|
|
|
|
if (isPressed())
|
|
|
|
state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_PRESSED);
|
|
|
|
|
|
|
|
if (isHovered())
|
|
|
|
state = static_cast<StyleSpec::State>(state | StyleSpec::STATE_HOVERED);
|
|
|
|
|
|
|
|
setFromStyle(StyleSpec::getStyleFromStatePropagation(Styles, state));
|
2019-03-15 19:39:23 +01:00
|
|
|
}
|
2019-12-09 21:06:51 +01:00
|
|
|
|
|
|
|
//! Set element properties from a StyleSpec
|
2020-04-11 22:39:30 +02:00
|
|
|
void GUIButton::setFromStyle(const StyleSpec& style)
|
2019-12-09 21:06:51 +01:00
|
|
|
{
|
2020-04-11 22:39:30 +02:00
|
|
|
bool hovered = (style.getState() & StyleSpec::STATE_HOVERED) != 0;
|
|
|
|
bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;
|
|
|
|
|
2019-12-09 21:06:51 +01:00
|
|
|
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
|
|
|
|
setColor(style.getColor(StyleSpec::BGCOLOR));
|
2020-04-11 22:39:30 +02:00
|
|
|
|
|
|
|
// If we have a propagated hover/press color, we need to automatically
|
|
|
|
// lighten/darken it
|
|
|
|
if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {
|
|
|
|
if (pressed) {
|
2020-08-29 17:41:19 +02:00
|
|
|
BgColor = multiplyColorValue(BgColor, COLOR_PRESSED_MOD);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 4; i++)
|
|
|
|
Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
|
2020-04-11 22:39:30 +02:00
|
|
|
} else if (hovered) {
|
2020-08-29 17:41:19 +02:00
|
|
|
BgColor = multiplyColorValue(BgColor, COLOR_HOVERED_MOD);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 4; i++)
|
|
|
|
Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
|
2020-04-11 22:39:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2020-08-29 17:41:19 +02:00
|
|
|
BgColor = video::SColor(255, 255, 255, 255);
|
2020-04-11 22:39:30 +02:00
|
|
|
for (size_t i = 0; i < 4; i++) {
|
|
|
|
video::SColor base =
|
|
|
|
Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
|
|
|
|
if (pressed) {
|
|
|
|
Colors[i] = multiplyColorValue(base, COLOR_PRESSED_MOD);
|
|
|
|
} else if (hovered) {
|
|
|
|
Colors[i] = multiplyColorValue(base, COLOR_HOVERED_MOD);
|
|
|
|
} else {
|
|
|
|
Colors[i] = base;
|
|
|
|
}
|
|
|
|
}
|
2019-12-09 21:06:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
|
|
|
|
setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
|
2020-04-11 22:39:30 +02:00
|
|
|
} else {
|
|
|
|
setOverrideColor(video::SColor(255,255,255,255));
|
|
|
|
OverrideColorEnabled = false;
|
2019-12-09 21:06:51 +01:00
|
|
|
}
|
2020-04-11 22:39:30 +02:00
|
|
|
setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
|
|
|
setDrawBorder(style.getBool(StyleSpec::BORDER, true));
|
2019-12-09 21:06:51 +01:00
|
|
|
setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
|
2020-07-12 09:48:50 +02:00
|
|
|
setOverrideFont(style.getFont());
|
2019-12-09 21:06:51 +01:00
|
|
|
|
|
|
|
if (style.isNotDefault(StyleSpec::BGIMG)) {
|
2020-04-11 22:39:30 +02:00
|
|
|
video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,
|
|
|
|
getTextureSource());
|
2019-12-09 21:06:51 +01:00
|
|
|
setImage(guiScalingImageButton(
|
2020-06-24 11:35:06 +02:00
|
|
|
Environment->getVideoDriver(), texture,
|
|
|
|
AbsoluteRect.getWidth(), AbsoluteRect.getHeight()));
|
2019-12-09 21:06:51 +01:00
|
|
|
setScaleImage(true);
|
2020-04-11 22:39:30 +02:00
|
|
|
} else {
|
|
|
|
setImage(nullptr);
|
2019-12-09 21:06:51 +01:00
|
|
|
}
|
2020-05-02 13:32:02 +02:00
|
|
|
|
2020-01-26 20:35:26 +01:00
|
|
|
BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);
|
2020-05-02 13:32:02 +02:00
|
|
|
|
|
|
|
// Child padding and offset
|
|
|
|
Padding = style.getRect(StyleSpec::PADDING, core::rect<s32>());
|
|
|
|
Padding = core::rect<s32>(
|
|
|
|
Padding.UpperLeftCorner + BgMiddle.UpperLeftCorner,
|
|
|
|
Padding.LowerRightCorner + BgMiddle.LowerRightCorner);
|
|
|
|
|
|
|
|
GUISkin* skin = dynamic_cast<GUISkin*>(Environment->getSkin());
|
|
|
|
core::vector2d<s32> defaultPressOffset(
|
|
|
|
skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
|
|
|
|
skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
|
|
|
|
ContentOffset = style.getVector2i(StyleSpec::CONTENT_OFFSET, isPressed()
|
|
|
|
? defaultPressOffset
|
|
|
|
: core::vector2d<s32>(0));
|
|
|
|
|
|
|
|
core::rect<s32> childBounds(
|
|
|
|
Padding.UpperLeftCorner.X + ContentOffset.X,
|
|
|
|
Padding.UpperLeftCorner.Y + ContentOffset.Y,
|
|
|
|
AbsoluteRect.getWidth() + Padding.LowerRightCorner.X + ContentOffset.X,
|
|
|
|
AbsoluteRect.getHeight() + Padding.LowerRightCorner.Y + ContentOffset.Y);
|
|
|
|
|
|
|
|
for (IGUIElement *child : getChildren()) {
|
|
|
|
child->setRelativePosition(childBounds);
|
|
|
|
}
|
2019-12-09 21:06:51 +01:00
|
|
|
}
|
2020-04-11 22:39:30 +02:00
|
|
|
|
|
|
|
//! Set the styles used for each state
|
|
|
|
void GUIButton::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES>& styles)
|
|
|
|
{
|
|
|
|
Styles = styles;
|
|
|
|
setFromState();
|
|
|
|
}
|
2019-03-15 19:39:23 +01:00
|
|
|
// END PATCH
|