aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt2
-rw-r--r--src/gui/StyleSpec.h163
-rw-r--r--src/gui/guiBox.cpp87
-rw-r--r--src/gui/guiBox.h11
-rw-r--r--src/gui/guiButton.cpp22
-rw-r--r--src/gui/guiButton.h1
-rw-r--r--src/gui/guiButtonItemImage.cpp1
-rw-r--r--src/gui/guiButtonItemImage.h1
-rw-r--r--src/gui/guiChatConsole.cpp9
-rw-r--r--src/gui/guiChatConsole.h6
-rw-r--r--src/gui/guiConfirmRegistration.cpp24
-rw-r--r--src/gui/guiEditBox.cpp911
-rw-r--r--src/gui/guiEditBox.h220
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp878
-rw-r--r--src/gui/guiEditBoxWithScrollbar.h140
-rw-r--r--src/gui/guiEngine.cpp23
-rw-r--r--src/gui/guiEngine.h13
-rw-r--r--src/gui/guiFormSpecMenu.cpp600
-rw-r--r--src/gui/guiFormSpecMenu.h62
-rw-r--r--src/gui/guiHyperText.cpp2
-rw-r--r--src/gui/guiInventoryList.cpp2
-rw-r--r--src/gui/guiKeyChangeMenu.cpp4
-rw-r--r--src/gui/guiMainMenu.h1
-rw-r--r--src/gui/guiPasswordChange.cpp21
-rw-r--r--src/gui/guiScene.cpp266
-rw-r--r--src/gui/guiScene.h86
-rw-r--r--src/gui/guiScrollBar.cpp2
-rw-r--r--src/gui/guiScrollBar.h1
-rw-r--r--src/gui/guiScrollContainer.cpp12
-rw-r--r--src/gui/guiScrollContainer.h2
-rw-r--r--src/gui/guiSkin.cpp2
-rw-r--r--src/gui/guiTable.cpp27
-rw-r--r--src/gui/guiTable.h6
-rw-r--r--src/gui/guiVolumeChange.cpp2
-rw-r--r--src/gui/intlGUIEditBox.cpp1241
-rw-r--r--src/gui/intlGUIEditBox.h154
-rw-r--r--src/gui/modalMenu.cpp261
-rw-r--r--src/gui/modalMenu.h37
-rw-r--r--src/gui/touchscreengui.cpp3
-rw-r--r--src/gui/touchscreengui.h1
40 files changed, 2599 insertions, 2708 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 147f445f4..fdd36914a 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -7,6 +7,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiButtonItemImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
@@ -15,6 +16,7 @@ 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}/guiScene.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h
index 3e842e826..fc92a861b 100644
--- a/src/gui/StyleSpec.h
+++ b/src/gui/StyleSpec.h
@@ -18,10 +18,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "client/tile.h" // ITextureSource
+#include "client/fontengine.h"
#include "debug.h"
#include "irrlichttypes_extrabloated.h"
#include "util/string.h"
+#include <algorithm>
#include <array>
+#include <vector>
#pragma once
@@ -46,6 +49,14 @@ public:
ALPHA,
CONTENT_OFFSET,
PADDING,
+ FONT,
+ FONT_SIZE,
+ COLORS,
+ BORDERCOLORS,
+ BORDERWIDTHS,
+ SOUND,
+ SPACING,
+ SIZE,
NUM_PROPERTIES,
NONE
};
@@ -98,6 +109,22 @@ public:
return CONTENT_OFFSET;
} else if (name == "padding") {
return PADDING;
+ } else if (name == "font") {
+ return FONT;
+ } else if (name == "font_size") {
+ return FONT_SIZE;
+ } else if (name == "colors") {
+ return COLORS;
+ } else if (name == "bordercolors") {
+ return BORDERCOLORS;
+ } else if (name == "borderwidths") {
+ return BORDERWIDTHS;
+ } else if (name == "sound") {
+ return SOUND;
+ } else if (name == "spacing") {
+ return SPACING;
+ } else if (name == "size") {
+ return SIZE;
} else {
return NONE;
}
@@ -179,6 +206,42 @@ public:
return color;
}
+ std::array<video::SColor, 4> getColorArray(Property prop,
+ std::array<video::SColor, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++) {
+ video::SColor color;
+ if (parseColorString(strs[i], color, false, 0xff))
+ def[i] = color;
+ }
+
+ return def;
+ }
+
+ std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ std::vector<std::string> strs;
+ if (!parseArray(val, strs))
+ return def;
+
+ for (size_t i = 0; i <= 3; i++)
+ def[i] = stoi(strs[i]);
+
+ return def;
+ }
+
irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const
{
const auto &val = properties[prop];
@@ -202,27 +265,81 @@ public:
return rect;
}
- irr::core::vector2d<s32> getVector2i(Property prop, irr::core::vector2d<s32> def) const
+ v2f32 getVector2f(Property prop, v2f32 def) const
{
const auto &val = properties[prop];
if (val.empty())
return def;
- irr::core::vector2d<s32> vec;
- if (!parseVector2i(val, &vec))
+ v2f32 vec;
+ if (!parseVector2f(val, &vec))
return def;
return vec;
}
- irr::core::vector2d<s32> getVector2i(Property prop) const
+ v2s32 getVector2i(Property prop, v2s32 def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ v2f32 vec;
+ if (!parseVector2f(val, &vec))
+ return def;
+
+ return v2s32(vec.X, vec.Y);
+ }
+
+ v2s32 getVector2i(Property prop) const
{
const auto &val = properties[prop];
FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- irr::core::vector2d<s32> vec;
- parseVector2i(val, &vec);
- return vec;
+ v2f32 vec;
+ parseVector2f(val, &vec);
+ return v2s32(vec.X, vec.Y);
+ }
+
+ gui::IGUIFont *getFont() const
+ {
+ FontSpec spec(FONT_SIZE_UNSPECIFIED, FM_Standard, false, false);
+
+ const std::string &font = properties[FONT];
+ const std::string &size = properties[FONT_SIZE];
+
+ if (font.empty() && size.empty())
+ return nullptr;
+
+ std::vector<std::string> modes = split(font, ',');
+
+ for (size_t i = 0; i < modes.size(); i++) {
+ if (modes[i] == "normal")
+ spec.mode = FM_Standard;
+ else if (modes[i] == "mono")
+ spec.mode = FM_Mono;
+ else if (modes[i] == "bold")
+ spec.bold = true;
+ else if (modes[i] == "italic")
+ spec.italic = true;
+ }
+
+ if (!size.empty()) {
+ int calc_size = 1;
+
+ if (size[0] == '*') {
+ std::string new_size = size.substr(1); // Remove '*' (invalid for stof)
+ calc_size = stof(new_size) * g_fontengine->getFontSize(spec.mode);
+ } else if (size[0] == '+' || size[0] == '-') {
+ calc_size = stoi(size) + g_fontengine->getFontSize(spec.mode);
+ } else {
+ calc_size = stoi(size);
+ }
+
+ spec.size = (unsigned)std::min(std::max(calc_size, 1), 999);
+ }
+
+ return g_fontengine->getFont(spec);
}
video::ITexture *getTexture(Property prop, ISimpleTextureSource *tsrc,
@@ -285,6 +402,24 @@ public:
}
private:
+ bool parseArray(const std::string &value, std::vector<std::string> &arr) const
+ {
+ std::vector<std::string> strs = split(value, ',');
+
+ if (strs.size() == 1) {
+ arr = {strs[0], strs[0], strs[0], strs[0]};
+ } else if (strs.size() == 2) {
+ arr = {strs[0], strs[1], strs[0], strs[1]};
+ } else if (strs.size() == 4) {
+ arr = strs;
+ } else {
+ warningstream << "Invalid array size (" << strs.size()
+ << " arguments): \"" << value << "\"" << std::endl;
+ return false;
+ }
+ return true;
+ }
+
bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const
{
irr::core::rect<s32> rect;
@@ -316,22 +451,20 @@ private:
return true;
}
- bool parseVector2i(const std::string &value, irr::core::vector2d<s32> *parsed_vec) const
+ bool parseVector2f(const std::string &value, v2f32 *parsed_vec) const
{
- irr::core::vector2d<s32> vec;
+ v2f32 vec;
std::vector<std::string> v_vector = split(value, ',');
if (v_vector.size() == 1) {
- s32 x = stoi(v_vector[0]);
+ f32 x = stof(v_vector[0]);
vec.X = x;
vec.Y = x;
} else if (v_vector.size() == 2) {
- s32 x = stoi(v_vector[0]);
- s32 y = stoi(v_vector[1]);
- vec.X = x;
- vec.Y = y;
+ vec.X = stof(v_vector[0]);
+ vec.Y = stof(v_vector[1]);
} else {
- warningstream << "Invalid vector2d string format: \"" << value
+ warningstream << "Invalid 2d vector string format: \"" << value
<< "\"" << std::endl;
return false;
}
diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp
index 7f329cc32..443f1064f 100644
--- a/src/gui/guiBox.cpp
+++ b/src/gui/guiBox.cpp
@@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiBox.h"
GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color) :
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths) :
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
- m_color(color)
+ m_colors(colors),
+ m_bordercolors(bordercolors),
+ m_borderwidths(borderwidths)
{
}
@@ -31,8 +36,82 @@ void GUIBox::draw()
if (!IsVisible)
return;
- Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect,
- &AbsoluteClippingRect);
+ std::array<s32, 4> negative_borders = {0, 0, 0, 0};
+ std::array<s32, 4> positive_borders = {0, 0, 0, 0};
+
+ for (size_t i = 0; i <= 3; i++) {
+ if (m_borderwidths[i] > 0)
+ positive_borders[i] = m_borderwidths[i];
+ else
+ negative_borders[i] = m_borderwidths[i];
+ }
+
+ v2s32 upperleft = AbsoluteRect.UpperLeftCorner;
+ v2s32 lowerright = AbsoluteRect.LowerRightCorner;
+
+ v2s32 topleft_border = {
+ upperleft.X - positive_borders[3],
+ upperleft.Y - positive_borders[0]
+ };
+ v2s32 topleft_rect = {
+ upperleft.X - negative_borders[3],
+ upperleft.Y - negative_borders[0]
+ };
+
+ v2s32 lowerright_border = {
+ lowerright.X + positive_borders[1],
+ lowerright.Y + positive_borders[2]
+ };
+ v2s32 lowerright_rect = {
+ lowerright.X + negative_borders[1],
+ lowerright.Y + negative_borders[2]
+ };
+
+ core::rect<s32> main_rect(
+ topleft_rect.X,
+ topleft_rect.Y,
+ lowerright_rect.X,
+ lowerright_rect.Y
+ );
+
+ std::array<core::rect<s32>, 4> border_rects;
+
+ border_rects[0] = core::rect<s32>(
+ topleft_border.X,
+ topleft_border.Y,
+ lowerright_border.X,
+ topleft_rect.Y
+ );
+
+ border_rects[1] = core::rect<s32>(
+ lowerright_rect.X,
+ topleft_rect.Y,
+ lowerright_border.X,
+ lowerright_rect.Y
+ );
+
+ border_rects[2] = core::rect<s32>(
+ topleft_border.X,
+ lowerright_rect.Y,
+ lowerright_border.X,
+ lowerright_border.Y
+ );
+
+ border_rects[3] = core::rect<s32>(
+ topleft_border.X,
+ topleft_rect.Y,
+ topleft_rect.X,
+ lowerright_rect.Y
+ );
+
+ video::IVideoDriver *driver = Environment->getVideoDriver();
+
+ driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3],
+ m_colors[2], &AbsoluteClippingRect);
+
+ for (size_t i = 0; i <= 3; i++)
+ driver->draw2DRectangle(m_bordercolors[i], border_rects[i],
+ &AbsoluteClippingRect);
IGUIElement::draw();
}
diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h
index 5306fdf65..ca8f83771 100644
--- a/src/gui/guiBox.h
+++ b/src/gui/guiBox.h
@@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include <vector>
+#include <array>
#include "irrlichttypes_extrabloated.h"
class GUIBox : public gui::IGUIElement
{
public:
GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- const core::rect<s32> &rectangle, const video::SColor &color);
+ const core::rect<s32> &rectangle,
+ const std::array<video::SColor, 4> &colors,
+ const std::array<video::SColor, 4> &bordercolors,
+ const std::array<s32, 4> &borderwidths);
virtual void draw() override;
private:
- video::SColor m_color;
+ std::array<video::SColor, 4> m_colors;
+ std::array<video::SColor, 4> m_bordercolors;
+ std::array<s32, 4> m_borderwidths;
};
diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp
index 6732a9233..b98e5de82 100644
--- a/src/gui/guiButton.cpp
+++ b/src/gui/guiButton.cpp
@@ -313,11 +313,12 @@ void GUIButton::draw()
// PATCH
video::ITexture* texture = ButtonImages[(u32)imageState].Texture;
+ video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor };
if (BgMiddle.getArea() == 0) {
driver->draw2DImage(texture,
ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
sourceRect, &AbsoluteClippingRect,
- 0, UseAlphaChannel);
+ image_colors, UseAlphaChannel);
} else {
core::rect<s32> middle = BgMiddle;
// `-x` is interpreted as `w - x`
@@ -327,7 +328,7 @@ void GUIButton::draw()
middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
- middle, &AbsoluteClippingRect);
+ middle, &AbsoluteClippingRect, image_colors);
}
// END PATCH
}
@@ -722,6 +723,8 @@ GUIButton* GUIButton::addButton(IGUIEnvironment *environment,
void GUIButton::setColor(video::SColor color)
{
+ BgColor = color;
+
float d = 0.65f;
for (size_t i = 0; i < 4; i++) {
video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
@@ -750,22 +753,26 @@ void GUIButton::setFromStyle(const StyleSpec& style)
bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
-
setColor(style.getColor(StyleSpec::BGCOLOR));
// If we have a propagated hover/press color, we need to automatically
// lighten/darken it
if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {
- for (size_t i = 0; i < 4; i++) {
if (pressed) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_PRESSED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
} else if (hovered) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_HOVERED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
}
- }
}
} else {
+ BgColor = video::SColor(255, 255, 255, 255);
for (size_t i = 0; i < 4; i++) {
video::SColor base =
Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
@@ -788,6 +795,7 @@ void GUIButton::setFromStyle(const StyleSpec& style)
setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
setDrawBorder(style.getBool(StyleSpec::BORDER, true));
setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
+ setOverrideFont(style.getFont());
if (style.isNotDefault(StyleSpec::BGIMG)) {
video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h
index 95fa1a2a1..4e1b04aac 100644
--- a/src/gui/guiButton.h
+++ b/src/gui/guiButton.h
@@ -338,5 +338,6 @@ private:
core::rect<s32> BgMiddle;
core::rect<s32> Padding;
core::vector2d<s32> ContentOffset;
+ video::SColor BgColor;
// END PATCH
};
diff --git a/src/gui/guiButtonItemImage.cpp b/src/gui/guiButtonItemImage.cpp
index d8b9042ac..39272fe37 100644
--- a/src/gui/guiButtonItemImage.cpp
+++ b/src/gui/guiButtonItemImage.cpp
@@ -39,7 +39,6 @@ GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment,
item, getActiveFont(), client);
sendToBack(m_image);
- m_item_name = item;
m_client = client;
}
diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h
index aad923bda..b90ac757e 100644
--- a/src/gui/guiButtonItemImage.h
+++ b/src/gui/guiButtonItemImage.h
@@ -42,7 +42,6 @@ public:
Client *client);
private:
- std::string m_item_name;
Client *m_client;
GUIItemImage *m_image;
};
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp
index 8de00c12f..ef471106d 100644
--- a/src/gui/guiChatConsole.cpp
+++ b/src/gui/guiChatConsole.cpp
@@ -136,11 +136,6 @@ void GUIChatConsole::closeConsoleAtOnce()
recalculateConsolePosition();
}
-f32 GUIChatConsole::getDesiredHeight() const
-{
- return m_desired_height_fraction;
-}
-
void GUIChatConsole::replaceAndAddToHistory(const std::wstring &line)
{
ChatPrompt& prompt = m_chat_backend->getPrompt();
@@ -545,7 +540,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
- std::string selected(wselected.begin(), wselected.end());
+ std::string selected = wide_to_utf8(wselected);
Environment->getOSOperator()->copyToClipboard(selected.c_str());
return true;
}
@@ -575,7 +570,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
if (prompt.getCursorLength() <= 0)
return true;
std::wstring wselected = prompt.getSelection();
- std::string selected(wselected.begin(), wselected.end());
+ std::string selected = wide_to_utf8(wselected);
Environment->getOSOperator()->copyToClipboard(selected.c_str());
prompt.cursorOperation(
ChatPrompt::CURSOROP_DELETE,
diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h
index 7be40e27c..896342ab0 100644
--- a/src/gui/guiChatConsole.h
+++ b/src/gui/guiChatConsole.h
@@ -55,10 +55,6 @@ public:
// Set whether to close the console after the user presses enter.
void setCloseOnEnter(bool close) { m_close_on_enter = close; }
- // Return the desired height (fraction of screen size)
- // Zero if the console is closed or getting closed
- f32 getDesiredHeight() const;
-
// Replace actual line when adding the actual to the history (if there is any)
void replaceAndAddToHistory(const std::wstring &line);
@@ -72,8 +68,6 @@ public:
// Irrlicht draw method
virtual void draw();
- bool canTakeFocus(gui::IGUIElement* element) { return false; }
-
virtual bool OnEvent(const SEvent& event);
virtual void setVisible(bool visible);
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
index 58ac42740..4a798c39b 100644
--- a/src/gui/guiConfirmRegistration.cpp
+++ b/src/gui/guiConfirmRegistration.cpp
@@ -73,7 +73,11 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
/*
Calculate new sizes and positions
*/
+#ifdef __ANDROID__
+ const float s = m_gui_scale * porting::getDisplayDensity() / 2;
+#else
const float s = m_gui_scale;
+#endif
DesiredRect = core::rect<s32>(
screensize.X / 2 - 600 * s / 2,
screensize.Y / 2 - 360 * s / 2,
@@ -188,8 +192,7 @@ void GUIConfirmRegistration::acceptInput()
bool GUIConfirmRegistration::processInput()
{
- std::wstring m_password_ws = narrow_to_wide(m_password);
- if (m_password_ws != m_pass_confirm) {
+ if (utf8_to_wide(m_password) != m_pass_confirm) {
gui::IGUIElement *e = getElementFromId(ID_message);
if (e)
e->setVisible(true);
@@ -222,7 +225,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."
+ infostream << "GUIConfirmRegistration: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
@@ -257,12 +260,19 @@ bool GUIConfirmRegistration::getAndroidUIInput()
if (!hasAndroidUIInput() || m_jni_field_name != "password")
return false;
- std::string text = porting::getInputDialogValue();
- gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
- if (e)
- e->setText(utf8_to_wide(text).c_str());
+ // still waiting
+ if (porting::getInputDialogState() == -1)
+ return true;
m_jni_field_name.clear();
+
+ gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
+
+ if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
+ return false;
+
+ std::string text = porting::getInputDialogValue();
+ e->setText(utf8_to_wide(text).c_str());
return false;
}
#endif
diff --git a/src/gui/guiEditBox.cpp b/src/gui/guiEditBox.cpp
new file mode 100644
index 000000000..79979dbc3
--- /dev/null
+++ b/src/gui/guiEditBox.cpp
@@ -0,0 +1,911 @@
+/*
+Minetest
+Copyright (C) 2021 Minetest
+
+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 "guiEditBox.h"
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+
+#include "porting.h"
+
+GUIEditBox::~GUIEditBox()
+{
+ if (m_override_font)
+ m_override_font->drop();
+
+ if (m_operator)
+ m_operator->drop();
+
+ if (m_vscrollbar)
+ m_vscrollbar->drop();
+}
+
+void GUIEditBox::setOverrideFont(IGUIFont *font)
+{
+ if (m_override_font == font)
+ return;
+
+ if (m_override_font)
+ m_override_font->drop();
+
+ m_override_font = font;
+
+ if (m_override_font)
+ m_override_font->grab();
+
+ breakText();
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont *GUIEditBox::getActiveFont() const
+{
+ if (m_override_font)
+ return m_override_font;
+ IGUISkin *skin = Environment->getSkin();
+ if (skin)
+ return skin->getFont();
+ return 0;
+}
+
+//! Sets another color for the text.
+void GUIEditBox::setOverrideColor(video::SColor color)
+{
+ m_override_color = color;
+ m_override_color_enabled = true;
+}
+
+video::SColor GUIEditBox::getOverrideColor() const
+{
+ return m_override_color;
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void GUIEditBox::enableOverrideColor(bool enable)
+{
+ m_override_color_enabled = enable;
+}
+
+//! Enables or disables word wrap
+void GUIEditBox::setWordWrap(bool enable)
+{
+ m_word_wrap = enable;
+ breakText();
+}
+
+//! Enables or disables newlines.
+void GUIEditBox::setMultiLine(bool enable)
+{
+ m_multiline = enable;
+}
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void GUIEditBox::setAutoScroll(bool enable)
+{
+ m_autoscroll = enable;
+}
+
+void GUIEditBox::setPasswordBox(bool password_box, wchar_t password_char)
+{
+ m_passwordbox = password_box;
+ if (m_passwordbox) {
+ m_passwordchar = password_char;
+ setMultiLine(false);
+ setWordWrap(false);
+ m_broken_text.clear();
+ }
+}
+
+//! Sets text justification
+void GUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+ m_halign = horizontal;
+ m_valign = vertical;
+}
+
+//! Sets the new caption of this element.
+void GUIEditBox::setText(const wchar_t *text)
+{
+ Text = text;
+ if (u32(m_cursor_pos) > Text.size())
+ m_cursor_pos = Text.size();
+ m_hscroll_pos = 0;
+ breakText();
+}
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void GUIEditBox::setMax(u32 max)
+{
+ m_max = max;
+
+ if (Text.size() > m_max && m_max != 0)
+ Text = Text.subString(0, m_max);
+}
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du GUIEditBox::getTextDimension()
+{
+ core::rect<s32> ret;
+
+ setTextRect(0);
+ ret = m_current_text_rect;
+
+ for (u32 i = 1; i < m_broken_text.size(); ++i) {
+ setTextRect(i);
+ ret.addInternalPoint(m_current_text_rect.UpperLeftCorner);
+ ret.addInternalPoint(m_current_text_rect.LowerRightCorner);
+ }
+
+ return core::dimension2du(ret.getSize());
+}
+
+//! Turns the border on or off
+void GUIEditBox::setDrawBorder(bool border)
+{
+ m_border = border;
+}
+
+void GUIEditBox::setWritable(bool can_write_text)
+{
+ m_writable = can_write_text;
+}
+
+//! set text markers
+void GUIEditBox::setTextMarkers(s32 begin, s32 end)
+{
+ if (begin != m_mark_begin || end != m_mark_end) {
+ m_mark_begin = begin;
+ m_mark_end = end;
+ sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+ }
+}
+
+//! send some gui event to parent
+void GUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+ if (Parent) {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = 0;
+ e.GUIEvent.EventType = type;
+
+ Parent->OnEvent(e);
+ }
+}
+
+//! called if an event happened.
+bool GUIEditBox::OnEvent(const SEvent &event)
+{
+ if (isEnabled()) {
+
+ switch (event.EventType) {
+ case EET_GUI_EVENT:
+ if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
+ if (event.GUIEvent.Caller == this) {
+ m_mouse_marking = false;
+ setTextMarkers(0, 0);
+ }
+ }
+ break;
+ case EET_KEY_INPUT_EVENT: {
+#if (defined(__linux__) || defined(__FreeBSD__)) || defined(__DragonFly__)
+ // ################################################################
+ // ValkaTR:
+ // This part is the difference from the original intlGUIEditBox
+ // It converts UTF-8 character into a UCS-2 (wchar_t)
+ wchar_t wc = L'_';
+ mbtowc(&wc, (char *)&event.KeyInput.Char,
+ sizeof(event.KeyInput.Char));
+
+ // printf( "char: %lc (%u) \r\n", wc, wc );
+
+ SEvent irrevent(event);
+ irrevent.KeyInput.Char = wc;
+ // ################################################################
+
+ if (processKey(irrevent))
+ return true;
+#else
+ if (processKey(event))
+ return true;
+#endif // defined(linux)
+
+ break;
+ }
+ case EET_MOUSE_INPUT_EVENT:
+ if (processMouse(event))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return IGUIElement::OnEvent(event);
+}
+
+bool GUIEditBox::processKey(const SEvent &event)
+{
+ if (!m_writable) {
+ return false;
+ }
+
+ if (!event.KeyInput.PressedDown)
+ return false;
+
+ bool text_changed = false;
+ s32 new_mark_begin = m_mark_begin;
+ s32 new_mark_end = m_mark_end;
+
+ // control shortcut handling
+ if (event.KeyInput.Control) {
+
+ // german backlash '\' entered with control + '?'
+ if (event.KeyInput.Char == '\\') {
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+
+ switch (event.KeyInput.Key) {
+ case KEY_KEY_A:
+ // select all
+ new_mark_begin = 0;
+ new_mark_end = Text.size();
+ break;
+ case KEY_KEY_C:
+ onKeyControlC(event);
+ break;
+ case KEY_KEY_X:
+ text_changed = onKeyControlX(event, new_mark_begin, new_mark_end);
+ break;
+ case KEY_KEY_V:
+ text_changed = onKeyControlV(event, new_mark_begin, new_mark_end);
+ break;
+ case KEY_HOME:
+ // move/highlight to start of text
+ if (event.KeyInput.Shift) {
+ new_mark_end = m_cursor_pos;
+ new_mark_begin = 0;
+ m_cursor_pos = 0;
+ } else {
+ m_cursor_pos = 0;
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+ break;
+ case KEY_END:
+ // move/highlight to end of text
+ if (event.KeyInput.Shift) {
+ new_mark_begin = m_cursor_pos;
+ new_mark_end = Text.size();
+ m_cursor_pos = 0;
+ } else {
+ m_cursor_pos = Text.size();
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+ break;
+ default:
+ return false;
+ }
+ } else {
+ switch (event.KeyInput.Key) {
+ case KEY_END: {
+ s32 p = Text.size();
+ if (m_word_wrap || m_multiline) {
+ p = getLineFromPos(m_cursor_pos);
+ p = m_broken_text_positions[p] +
+ (s32)m_broken_text[p].size();
+ if (p > 0 && (Text[p - 1] == L'\r' ||
+ Text[p - 1] == L'\n'))
+ p -= 1;
+ }
+
+ if (event.KeyInput.Shift) {
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+
+ new_mark_end = p;
+ } else {
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+ m_cursor_pos = p;
+ m_blink_start_time = porting::getTimeMs();
+ } break;
+ case KEY_HOME: {
+
+ s32 p = 0;
+ if (m_word_wrap || m_multiline) {
+ p = getLineFromPos(m_cursor_pos);
+ p = m_broken_text_positions[p];
+ }
+
+ if (event.KeyInput.Shift) {
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+ new_mark_end = p;
+ } else {
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+ m_cursor_pos = p;
+ m_blink_start_time = porting::getTimeMs();
+ } break;
+ case KEY_RETURN:
+ if (m_multiline) {
+ inputChar(L'\n');
+ } else {
+ calculateScrollPos();
+ sendGuiEvent(EGET_EDITBOX_ENTER);
+ }
+ return true;
+ case KEY_LEFT:
+ if (event.KeyInput.Shift) {
+ if (m_cursor_pos > 0) {
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+
+ new_mark_end = m_cursor_pos - 1;
+ }
+ } else {
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+
+ if (m_cursor_pos > 0)
+ m_cursor_pos--;
+ m_blink_start_time = porting::getTimeMs();
+ break;
+ case KEY_RIGHT:
+ if (event.KeyInput.Shift) {
+ if (Text.size() > (u32)m_cursor_pos) {
+ if (m_mark_begin == m_mark_end)
+ new_mark_begin = m_cursor_pos;
+
+ new_mark_end = m_cursor_pos + 1;
+ }
+ } else {
+ new_mark_begin = 0;
+ new_mark_end = 0;
+ }
+
+ if (Text.size() > (u32)m_cursor_pos)
+ m_cursor_pos++;
+ m_blink_start_time = porting::getTimeMs();
+ break;
+ case KEY_UP:
+ if (!onKeyUp(event, new_mark_begin, new_mark_end)) {
+ return false;
+ }
+ break;
+ case KEY_DOWN:
+ if (!onKeyDown(event, new_mark_begin, new_mark_end)) {
+ return false;
+ }
+ break;
+ case KEY_BACK:
+ text_changed = onKeyBack(event, new_mark_begin, new_mark_end);
+ break;
+
+ case KEY_DELETE:
+ text_changed = onKeyDelete(event, new_mark_begin, new_mark_end);
+ break;
+
+ case KEY_ESCAPE:
+ case KEY_TAB:
+ case KEY_SHIFT:
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ case KEY_F13:
+ case KEY_F14:
+ case KEY_F15:
+ case KEY_F16:
+ case KEY_F17:
+ case KEY_F18:
+ case KEY_F19:
+ case KEY_F20:
+ case KEY_F21:
+ case KEY_F22:
+ case KEY_F23:
+ case KEY_F24:
+ // ignore these keys
+ return false;
+
+ default:
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+ }
+
+ // Set new text markers
+ setTextMarkers(new_mark_begin, new_mark_end);
+
+ // break the text if it has changed
+ if (text_changed) {
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ }
+
+ calculateScrollPos();
+
+ return true;
+}
+
+bool GUIEditBox::onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ // clang-format off
+ if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
+ s32 lineNo = getLineFromPos(m_cursor_pos);
+ s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
+ (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
+ if (lineNo > 0) {
+ s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
+ if ((s32)m_broken_text[lineNo - 1].size() < cp) {
+ m_cursor_pos = m_broken_text_positions[lineNo - 1] +
+ core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
+ }
+ else
+ m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
+ }
+
+ if (event.KeyInput.Shift) {
+ mark_begin = mb;
+ mark_end = m_cursor_pos;
+ } else {
+ mark_begin = 0;
+ mark_end = 0;
+ }
+
+ return true;
+ }
+
+ // clang-format on
+ return false;
+}
+
+bool GUIEditBox::onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ // clang-format off
+ if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
+ s32 lineNo = getLineFromPos(m_cursor_pos);
+ s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos :
+ (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
+ if (lineNo < (s32)m_broken_text.size() - 1) {
+ s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
+ if ((s32)m_broken_text[lineNo + 1].size() < cp) {
+ m_cursor_pos = m_broken_text_positions[lineNo + 1] +
+ core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
+ }
+ else
+ m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
+ }
+
+ if (event.KeyInput.Shift) {
+ mark_begin = mb;
+ mark_end = m_cursor_pos;
+ } else {
+ mark_begin = 0;
+ mark_end = 0;
+ }
+
+ return true;
+ }
+
+ // clang-format on
+ return false;
+}
+
+void GUIEditBox::onKeyControlC(const SEvent &event)
+{
+ // copy to clipboard
+ if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
+ return;
+
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ core::stringc s;
+ s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ m_operator->copyToClipboard(s.c_str());
+}
+
+bool GUIEditBox::onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ // First copy to clipboard
+ onKeyControlC(event);
+
+ if (m_passwordbox || !m_operator || m_mark_begin == m_mark_end)
+ return false;
+
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ // Now remove from box if enabled
+ if (isEnabled()) {
+ // delete
+ core::stringw s;
+ s = Text.subString(0, realmbgn);
+ s.append(Text.subString(realmend, Text.size() - realmend));
+ Text = s;
+
+ m_cursor_pos = realmbgn;
+ mark_begin = 0;
+ mark_end = 0;
+ return true;
+ }
+
+ return false;
+}
+
+bool GUIEditBox::onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ if (!isEnabled())
+ return false;
+
+ // paste from the clipboard
+ if (!m_operator)
+ return false;
+
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ // add new character
+ if (const c8 *p = m_operator->getTextFromClipboard()) {
+ if (m_mark_begin == m_mark_end) {
+ // insert text
+ core::stringw s = Text.subString(0, m_cursor_pos);
+ s.append(p);
+ s.append(Text.subString(
+ m_cursor_pos, Text.size() - m_cursor_pos));
+
+ if (!m_max || s.size() <= m_max) {
+ Text = s;
+ s = p;
+ m_cursor_pos += s.size();
+ }
+ } else {
+ // replace text
+
+ core::stringw s = Text.subString(0, realmbgn);
+ s.append(p);
+ s.append(Text.subString(realmend, Text.size() - realmend));
+
+ if (!m_max || s.size() <= m_max) {
+ Text = s;
+ s = p;
+ m_cursor_pos = realmbgn + s.size();
+ }
+ }
+ }
+
+ mark_begin = 0;
+ mark_end = 0;
+ return true;
+}
+
+bool GUIEditBox::onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ if (!isEnabled() || Text.empty())
+ return false;
+
+ core::stringw s;
+
+ if (m_mark_begin != m_mark_end) {
+ // delete marked text
+ const s32 realmbgn =
+ m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend =
+ m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ s = Text.subString(0, realmbgn);
+ s.append(Text.subString(realmend, Text.size() - realmend));
+ Text = s;
+
+ m_cursor_pos = realmbgn;
+ } else {
+ // delete text behind cursor
+ if (m_cursor_pos > 0)
+ s = Text.subString(0, m_cursor_pos - 1);
+ else
+ s = L"";
+ s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
+ Text = s;
+ --m_cursor_pos;
+ }
+
+ if (m_cursor_pos < 0)
+ m_cursor_pos = 0;
+ m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
+ mark_begin = 0;
+ mark_end = 0;
+ return true;
+}
+
+bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end)
+{
+ if (!isEnabled() || Text.empty())
+ return false;
+
+ core::stringw s;
+
+ if (m_mark_begin != m_mark_end) {
+ // delete marked text
+ const s32 realmbgn =
+ m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend =
+ m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ s = Text.subString(0, realmbgn);
+ s.append(Text.subString(realmend, Text.size() - realmend));
+ Text = s;
+
+ m_cursor_pos = realmbgn;
+ } else {
+ // delete text before cursor
+ s = Text.subString(0, m_cursor_pos);
+ s.append(Text.subString(
+ m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
+ Text = s;
+ }
+
+ if (m_cursor_pos > (s32)Text.size())
+ m_cursor_pos = (s32)Text.size();
+
+ m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
+ mark_begin = 0;
+ mark_end = 0;
+ return true;
+}
+
+void GUIEditBox::inputChar(wchar_t c)
+{
+ if (!isEnabled() || !m_writable)
+ return;
+
+ if (c != 0) {
+ if (Text.size() < m_max || m_max == 0) {
+ core::stringw s;
+
+ if (m_mark_begin != m_mark_end) {
+ // clang-format off
+ // replace marked text
+ s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ s = Text.subString(0, real_begin);
+ s.append(c);
+ s.append(Text.subString(real_end, Text.size() - real_end));
+ Text = s;
+ m_cursor_pos = real_begin + 1;
+ // clang-format on
+ } else {
+ // add new character
+ s = Text.subString(0, m_cursor_pos);
+ s.append(c);
+ s.append(Text.subString(m_cursor_pos,
+ Text.size() - m_cursor_pos));
+ Text = s;
+ ++m_cursor_pos;
+ }
+
+ m_blink_start_time = porting::getTimeMs();
+ setTextMarkers(0, 0);
+ }
+ }
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ calculateScrollPos();
+}
+
+bool GUIEditBox::processMouse(const SEvent &event)
+{
+ switch (event.MouseInput.Event) {
+ case irr::EMIE_LMOUSE_LEFT_UP:
+ if (Environment->hasFocus(this)) {
+ m_cursor_pos = getCursorPos(
+ event.MouseInput.X, event.MouseInput.Y);
+ if (m_mouse_marking) {
+ setTextMarkers(m_mark_begin, m_cursor_pos);
+ }
+ m_mouse_marking = false;
+ calculateScrollPos();
+ return true;
+ }
+ break;
+ case irr::EMIE_MOUSE_MOVED: {
+ if (m_mouse_marking) {
+ m_cursor_pos = getCursorPos(
+ event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers(m_mark_begin, m_cursor_pos);
+ calculateScrollPos();
+ return true;
+ }
+ } break;
+ case EMIE_LMOUSE_PRESSED_DOWN:
+
+ if (!Environment->hasFocus(this)) {
+ m_blink_start_time = porting::getTimeMs();
+ m_mouse_marking = true;
+ m_cursor_pos = getCursorPos(
+ event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers(m_cursor_pos, m_cursor_pos);
+ calculateScrollPos();
+ return true;
+ } else {
+ if (!AbsoluteClippingRect.isPointInside(core::position2d<s32>(
+ event.MouseInput.X, event.MouseInput.Y))) {
+ return false;
+ } else {
+ // move cursor
+ m_cursor_pos = getCursorPos(
+ event.MouseInput.X, event.MouseInput.Y);
+
+ s32 newMarkBegin = m_mark_begin;
+ if (!m_mouse_marking)
+ newMarkBegin = m_cursor_pos;
+
+ m_mouse_marking = true;
+ setTextMarkers(newMarkBegin, m_cursor_pos);
+ calculateScrollPos();
+ return true;
+ }
+ }
+ case EMIE_MOUSE_WHEEL:
+ if (m_vscrollbar && m_vscrollbar->isVisible()) {
+ s32 pos = m_vscrollbar->getPos();
+ s32 step = m_vscrollbar->getSmallStep();
+ m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+s32 GUIEditBox::getLineFromPos(s32 pos)
+{
+ if (!m_word_wrap && !m_multiline)
+ return 0;
+
+ s32 i = 0;
+ while (i < (s32)m_broken_text_positions.size()) {
+ if (m_broken_text_positions[i] > pos)
+ return i - 1;
+ ++i;
+ }
+ return (s32)m_broken_text_positions.size() - 1;
+}
+
+void GUIEditBox::updateVScrollBar()
+{
+ if (!m_vscrollbar) {
+ return;
+ }
+
+ // OnScrollBarChanged(...)
+ if (m_vscrollbar->getPos() != m_vscroll_pos) {
+ s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
+ m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
+ m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
+
+ s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
+ 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
+ m_vscroll_pos = m_vscrollbar->getPos();
+ }
+ }
+
+ // check if a vertical scrollbar is needed ?
+ if (getTextDimension().Height > (u32)m_frame_rect.getHeight()) {
+ m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
+
+ 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()) {
+ m_vscrollbar->setVisible(true);
+ }
+ } else {
+ if (m_vscrollbar->isVisible()) {
+ m_vscrollbar->setVisible(false);
+ m_vscroll_pos = 0;
+ m_vscrollbar->setPos(0);
+ m_vscrollbar->setMax(1);
+ m_vscrollbar->setPageSize(s32(getTextDimension().Height));
+ }
+ }
+}
+
+void GUIEditBox::deserializeAttributes(
+ io::IAttributes *in, io::SAttributeReadWriteOptions *options = 0)
+{
+ IGUIEditBox::deserializeAttributes(in, options);
+
+ setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+ enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+ setMax(in->getAttributeAsInt("MaxChars"));
+ setWordWrap(in->getAttributeAsBool("WordWrap"));
+ setMultiLine(in->getAttributeAsBool("MultiLine"));
+ setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+ core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+ if (ch.empty())
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+ else
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+ setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
+ "HTextAlign", GUIAlignmentNames),
+ (EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
+ "VTextAlign", GUIAlignmentNames));
+
+ setWritable(in->getAttributeAsBool("Writable"));
+ // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+//! Writes attributes of the element.
+void GUIEditBox::serializeAttributes(
+ io::IAttributes *out, io::SAttributeReadWriteOptions *options = 0) const
+{
+ // IGUIEditBox::serializeAttributes(out,options);
+
+ out->addBool("OverrideColorEnabled", m_override_color_enabled);
+ out->addColor("OverrideColor", m_override_color);
+ // out->addFont("OverrideFont",m_override_font);
+ out->addInt("MaxChars", m_max);
+ out->addBool("WordWrap", m_word_wrap);
+ out->addBool("MultiLine", m_multiline);
+ out->addBool("AutoScroll", m_autoscroll);
+ out->addBool("PasswordBox", m_passwordbox);
+ core::stringw ch = L" ";
+ ch[0] = m_passwordchar;
+ out->addString("PasswordChar", ch.c_str());
+ out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
+ out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
+ out->addBool("Writable", m_writable);
+
+ IGUIEditBox::serializeAttributes(out, options);
+}
diff --git a/src/gui/guiEditBox.h b/src/gui/guiEditBox.h
new file mode 100644
index 000000000..c616d75d1
--- /dev/null
+++ b/src/gui/guiEditBox.h
@@ -0,0 +1,220 @@
+/*
+Minetest
+Copyright (C) 2021 Minetest
+
+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.
+*/
+
+#pragma once
+
+#include "irrlichttypes.h"
+#include "IGUIEditBox.h"
+#include "IOSOperator.h"
+#include "guiScrollBar.h"
+#include <vector>
+
+using namespace irr;
+using namespace irr::gui;
+
+class GUIEditBox : public IGUIEditBox
+{
+public:
+ GUIEditBox(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
+ core::rect<s32> rectangle, bool border, bool writable) :
+ IGUIEditBox(environment, parent, id, rectangle),
+ m_border(border), m_writable(writable), m_frame_rect(rectangle)
+ {
+ }
+
+ virtual ~GUIEditBox();
+
+ //! Sets another skin independent font.
+ virtual void setOverrideFont(IGUIFont *font = 0);
+
+ virtual IGUIFont *getOverrideFont() const { return m_override_font; }
+
+ //! Get the font which is used right now for drawing
+ /** Currently this is the override font when one is set and the
+ font of the active skin otherwise */
+ virtual IGUIFont *getActiveFont() const;
+
+ //! Sets another color for the text.
+ virtual void setOverrideColor(video::SColor color);
+
+ //! Gets the override color
+ virtual video::SColor getOverrideColor() const;
+
+ //! Sets if the text should use the overide color or the
+ //! color in the gui skin.
+ virtual void enableOverrideColor(bool enable);
+
+ //! Checks if an override color is enabled
+ /** \return true if the override color is enabled, false otherwise */
+ virtual bool isOverrideColorEnabled(void) const
+ {
+ return m_override_color_enabled;
+ }
+
+ //! Enables or disables word wrap for using the edit box as multiline text editor.
+ virtual void setWordWrap(bool enable);
+
+ //! Checks if word wrap is enabled
+ //! \return true if word wrap is enabled, false otherwise
+ virtual bool isWordWrapEnabled() const { return m_word_wrap; }
+
+ //! Turns the border on or off
+ virtual void setDrawBorder(bool border);
+
+ virtual bool isDrawBorderEnabled() const { return m_border; }
+
+ //! Enables or disables newlines.
+ /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+ instead a newline character will be inserted. */
+ virtual void setMultiLine(bool enable);
+
+ //! Checks if multi line editing is enabled
+ //! \return true if mult-line is enabled, false otherwise
+ virtual bool isMultiLineEnabled() const { return m_multiline; }
+
+ //! Enables or disables automatic scrolling with cursor position
+ //! \param enable: If set to true, the text will move around with the cursor
+ //! position
+ virtual void setAutoScroll(bool enable);
+
+ //! Checks to see if automatic scrolling is enabled
+ //! \return true if automatic scrolling is enabled, false if not
+ virtual bool isAutoScrollEnabled() const { return m_autoscroll; }
+
+ //! Sets whether the edit box is a password box. Setting this to true will
+ /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+ \param passwordBox: true to enable password, false to disable
+ \param passwordChar: the character that is displayed instead of letters */
+ virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+ //! Returns true if the edit box is currently a password box.
+ virtual bool isPasswordBox() const { return m_passwordbox; }
+
+ //! Sets text justification
+ virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+ //! Sets the new caption of this element.
+ virtual void setText(const wchar_t *text);
+
+ //! Sets the maximum amount of characters which may be entered in the box.
+ //! \param max: Maximum amount of characters. If 0, the character amount is
+ //! infinity.
+ virtual void setMax(u32 max);
+
+ //! Returns maximum amount of characters, previously set by setMax();
+ virtual u32 getMax() const { return m_max; }
+
+ //! Gets the size area of the text in the edit box
+ //! \return Returns the size in pixels of the text
+ virtual core::dimension2du getTextDimension();
+
+ //! set true if this EditBox is writable
+ virtual void setWritable(bool can_write_text);
+
+ //! called if an event happened.
+ virtual bool OnEvent(const SEvent &event);
+
+ //! Writes attributes of the element.
+ virtual void serializeAttributes(io::IAttributes *out,
+ io::SAttributeReadWriteOptions *options) const;
+
+ //! Reads attributes of the element
+ virtual void deserializeAttributes(
+ io::IAttributes *in, io::SAttributeReadWriteOptions *options);
+
+protected:
+ virtual void breakText() = 0;
+
+ //! sets the area of the given line
+ virtual void setTextRect(s32 line) = 0;
+
+ //! set text markers
+ void setTextMarkers(s32 begin, s32 end);
+
+ //! send some gui event to parent
+ void sendGuiEvent(EGUI_EVENT_TYPE type);
+
+ //! calculates the current scroll position
+ virtual void calculateScrollPos() = 0;
+
+ virtual s32 getCursorPos(s32 x, s32 y) = 0;
+
+ bool processKey(const SEvent &event);
+ virtual void inputChar(wchar_t c);
+
+ //! returns the line number that the cursor is on
+ s32 getLineFromPos(s32 pos);
+
+ //! update the vertical scrollBar (visibilty & position)
+ void updateVScrollBar();
+
+ gui::IGUIFont *m_override_font = nullptr;
+
+ bool m_override_color_enabled = false;
+ bool m_word_wrap = false;
+ bool m_multiline = false;
+ bool m_autoscroll = true;
+
+ bool m_border;
+
+ bool m_passwordbox = false;
+ wchar_t m_passwordchar = L'*';
+
+ std::vector<core::stringw> m_broken_text;
+ std::vector<s32> m_broken_text_positions;
+
+ EGUI_ALIGNMENT m_halign = EGUIA_UPPERLEFT;
+ EGUI_ALIGNMENT m_valign = EGUIA_CENTER;
+
+ u32 m_blink_start_time = 0;
+ s32 m_cursor_pos = 0;
+ s32 m_hscroll_pos = 0;
+ s32 m_vscroll_pos = 0; // scroll position in characters
+ u32 m_max = 0;
+
+ video::SColor m_override_color = video::SColor(101, 255, 255, 255);
+
+ core::rect<s32> m_current_text_rect = core::rect<s32>(0, 0, 1, 1);
+
+ bool m_writable;
+
+ bool m_mouse_marking = false;
+
+ s32 m_mark_begin = 0;
+ s32 m_mark_end = 0;
+
+ gui::IGUIFont *m_last_break_font = nullptr;
+ IOSOperator *m_operator = nullptr;
+
+ core::rect<s32> m_frame_rect; // temporary values
+
+ u32 m_scrollbar_width = 0;
+ GUIScrollBar *m_vscrollbar = nullptr;
+
+private:
+ bool processMouse(const SEvent &event);
+
+ bool onKeyUp(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+ bool onKeyDown(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+ void onKeyControlC(const SEvent &event);
+ bool onKeyControlX(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+ bool onKeyControlV(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+ bool onKeyBack(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+ bool onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end);
+};
diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
index 442406688..c72070787 100644
--- a/src/gui/guiEditBoxWithScrollbar.cpp
+++ b/src/gui/guiEditBoxWithScrollbar.cpp
@@ -22,20 +22,12 @@ optional? dragging selected text
numerical
*/
-
//! constructor
GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool border,
IGUIEnvironment* environment, IGUIElement* parent, s32 id,
const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
- : IGUIEditBox(environment, parent, id, rectangle), m_mouse_marking(false),
- m_border(border), m_background(true), m_override_color_enabled(false), m_mark_begin(0), m_mark_end(0),
- m_override_color(video::SColor(101, 255, 255, 255)), m_override_font(0), m_last_break_font(0),
- m_operator(0), m_blink_start_time(0), m_cursor_pos(0), m_hscroll_pos(0), m_vscroll_pos(0), m_max(0),
- m_word_wrap(false), m_multiline(false), m_autoscroll(true), m_passwordbox(false),
- m_passwordchar(L'*'), m_halign(EGUIA_UPPERLEFT), m_valign(EGUIA_CENTER),
- m_current_text_rect(0, 0, 1, 1), m_frame_rect(rectangle),
- m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable),
- m_bg_color_used(false)
+ : GUIEditBox(environment, parent, id, rectangle, border, writable),
+ m_background(true), m_bg_color_used(false)
{
#ifdef _DEBUG
setDebugName("GUIEditBoxWithScrollBar");
@@ -65,99 +57,12 @@ GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool borde
setWritable(writable);
}
-
-//! destructor
-GUIEditBoxWithScrollBar::~GUIEditBoxWithScrollBar()
-{
- if (m_override_font)
- m_override_font->drop();
-
- if (m_operator)
- m_operator->drop();
-
- if (m_vscrollbar)
- m_vscrollbar->drop();
-}
-
-
-//! Sets another skin independent font.
-void GUIEditBoxWithScrollBar::setOverrideFont(IGUIFont* font)
-{
- if (m_override_font == font)
- return;
-
- if (m_override_font)
- m_override_font->drop();
-
- m_override_font = font;
-
- if (m_override_font)
- m_override_font->grab();
-
- breakText();
-}
-
-//! Gets the override font (if any)
-IGUIFont * GUIEditBoxWithScrollBar::getOverrideFont() const
-{
- return m_override_font;
-}
-
-//! Get the font which is used right now for drawing
-IGUIFont* GUIEditBoxWithScrollBar::getActiveFont() const
-{
- if (m_override_font)
- return m_override_font;
- IGUISkin* skin = Environment->getSkin();
- if (skin)
- return skin->getFont();
- return 0;
-}
-
-//! Sets another color for the text.
-void GUIEditBoxWithScrollBar::setOverrideColor(video::SColor color)
-{
- m_override_color = color;
- m_override_color_enabled = true;
-}
-
-
-video::SColor GUIEditBoxWithScrollBar::getOverrideColor() const
-{
- return m_override_color;
-}
-
-
-//! Turns the border on or off
-void GUIEditBoxWithScrollBar::setDrawBorder(bool border)
-{
- m_border = border;
-}
-
//! Sets whether to draw the background
void GUIEditBoxWithScrollBar::setDrawBackground(bool draw)
{
m_background = draw;
}
-//! Sets if the text should use the overide color or the color in the gui skin.
-void GUIEditBoxWithScrollBar::enableOverrideColor(bool enable)
-{
- m_override_color_enabled = enable;
-}
-
-bool GUIEditBoxWithScrollBar::isOverrideColorEnabled() const
-{
- return m_override_color_enabled;
-}
-
-//! Enables or disables word wrap
-void GUIEditBoxWithScrollBar::setWordWrap(bool enable)
-{
- m_word_wrap = enable;
- breakText();
-}
-
void GUIEditBoxWithScrollBar::updateAbsolutePosition()
{
@@ -170,486 +75,6 @@ void GUIEditBoxWithScrollBar::updateAbsolutePosition()
}
}
-//! Checks if word wrap is enabled
-bool GUIEditBoxWithScrollBar::isWordWrapEnabled() const
-{
- return m_word_wrap;
-}
-
-
-//! Enables or disables newlines.
-void GUIEditBoxWithScrollBar::setMultiLine(bool enable)
-{
- m_multiline = enable;
-}
-
-
-//! Checks if multi line editing is enabled
-bool GUIEditBoxWithScrollBar::isMultiLineEnabled() const
-{
- return m_multiline;
-}
-
-
-void GUIEditBoxWithScrollBar::setPasswordBox(bool password_box, wchar_t password_char)
-{
- m_passwordbox = password_box;
- if (m_passwordbox) {
- m_passwordchar = password_char;
- setMultiLine(false);
- setWordWrap(false);
- m_broken_text.clear();
- }
-}
-
-
-bool GUIEditBoxWithScrollBar::isPasswordBox() const
-{
- return m_passwordbox;
-}
-
-
-//! Sets text justification
-void GUIEditBoxWithScrollBar::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
-{
- m_halign = horizontal;
- m_valign = vertical;
-}
-
-
-//! called if an event happened.
-bool GUIEditBoxWithScrollBar::OnEvent(const SEvent& event)
-{
- if (isEnabled()) {
- switch (event.EventType)
- {
- case EET_GUI_EVENT:
- if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
- if (event.GUIEvent.Caller == this) {
- m_mouse_marking = false;
- setTextMarkers(0, 0);
- }
- }
- break;
- case EET_KEY_INPUT_EVENT:
- if (processKey(event))
- return true;
- break;
- case EET_MOUSE_INPUT_EVENT:
- if (processMouse(event))
- return true;
- break;
- default:
- break;
- }
- }
-
- return IGUIElement::OnEvent(event);
-}
-
-
-bool GUIEditBoxWithScrollBar::processKey(const SEvent& event)
-{
- if (!m_writable) {
- return false;
- }
-
- if (!event.KeyInput.PressedDown)
- return false;
-
- bool text_changed = false;
- s32 new_mark_begin = m_mark_begin;
- s32 new_mark_end = m_mark_end;
-
- // control shortcut handling
-
- if (event.KeyInput.Control) {
-
- // german backlash '\' entered with control + '?'
- if (event.KeyInput.Char == '\\') {
- inputChar(event.KeyInput.Char);
- return true;
- }
-
- switch (event.KeyInput.Key) {
- case KEY_KEY_A:
- // select all
- new_mark_begin = 0;
- new_mark_end = Text.size();
- break;
- case KEY_KEY_C:
- // copy to clipboard
- if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end)
- {
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- core::stringc s;
- s = Text.subString(realmbgn, realmend - realmbgn).c_str();
- m_operator->copyToClipboard(s.c_str());
- }
- break;
- case KEY_KEY_X:
- // cut to the clipboard
- if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end) {
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- // copy
- core::stringc sc;
- sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
- m_operator->copyToClipboard(sc.c_str());
-
- if (isEnabled())
- {
- // delete
- core::stringw s;
- s = Text.subString(0, realmbgn);
- s.append(Text.subString(realmend, Text.size() - realmend));
- Text = s;
-
- m_cursor_pos = realmbgn;
- new_mark_begin = 0;
- new_mark_end = 0;
- text_changed = true;
- }
- }
- break;
- case KEY_KEY_V:
- if (!isEnabled())
- break;
-
- // paste from the clipboard
- if (m_operator) {
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- // add new character
- const c8* p = m_operator->getTextFromClipboard();
- if (p) {
- if (m_mark_begin == m_mark_end) {
- // insert text
- core::stringw s = Text.subString(0, m_cursor_pos);
- s.append(p);
- s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
-
- if (!m_max || s.size() <= m_max) // thx to Fish FH for fix
- {
- Text = s;
- s = p;
- m_cursor_pos += s.size();
- }
- } else {
- // replace text
-
- core::stringw s = Text.subString(0, realmbgn);
- s.append(p);
- s.append(Text.subString(realmend, Text.size() - realmend));
-
- if (!m_max || s.size() <= m_max) // thx to Fish FH for fix
- {
- Text = s;
- s = p;
- m_cursor_pos = realmbgn + s.size();
- }
- }
- }
-
- new_mark_begin = 0;
- new_mark_end = 0;
- text_changed = true;
- }
- break;
- case KEY_HOME:
- // move/highlight to start of text
- if (event.KeyInput.Shift) {
- new_mark_end = m_cursor_pos;
- new_mark_begin = 0;
- m_cursor_pos = 0;
- } else {
- m_cursor_pos = 0;
- new_mark_begin = 0;
- new_mark_end = 0;
- }
- break;
- case KEY_END:
- // move/highlight to end of text
- if (event.KeyInput.Shift) {
- new_mark_begin = m_cursor_pos;
- new_mark_end = Text.size();
- m_cursor_pos = 0;
- } else {
- m_cursor_pos = Text.size();
- new_mark_begin = 0;
- new_mark_end = 0;
- }
- break;
- default:
- return false;
- }
- }
- // default keyboard handling
- else
- switch (event.KeyInput.Key) {
- case KEY_END:
- {
- s32 p = Text.size();
- if (m_word_wrap || m_multiline) {
- p = getLineFromPos(m_cursor_pos);
- p = m_broken_text_positions[p] + (s32)m_broken_text[p].size();
- if (p > 0 && (Text[p - 1] == L'\r' || Text[p - 1] == L'\n'))
- p -= 1;
- }
-
- if (event.KeyInput.Shift) {
- if (m_mark_begin == m_mark_end)
- new_mark_begin = m_cursor_pos;
-
- new_mark_end = p;
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
- m_cursor_pos = p;
- m_blink_start_time = porting::getTimeMs();
- }
- break;
- case KEY_HOME:
- {
-
- s32 p = 0;
- if (m_word_wrap || m_multiline) {
- p = getLineFromPos(m_cursor_pos);
- p = m_broken_text_positions[p];
- }
-
- if (event.KeyInput.Shift) {
- if (m_mark_begin == m_mark_end)
- new_mark_begin = m_cursor_pos;
- new_mark_end = p;
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
- m_cursor_pos = p;
- m_blink_start_time = porting::getTimeMs();
- }
- break;
- case KEY_RETURN:
- if (m_multiline) {
- inputChar(L'\n');
- } else {
- calculateScrollPos();
- sendGuiEvent(EGET_EDITBOX_ENTER);
- }
- return true;
- case KEY_LEFT:
-
- if (event.KeyInput.Shift) {
- if (m_cursor_pos > 0) {
- if (m_mark_begin == m_mark_end)
- new_mark_begin = m_cursor_pos;
-
- new_mark_end = m_cursor_pos - 1;
- }
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
-
- if (m_cursor_pos > 0)
- m_cursor_pos--;
- m_blink_start_time = porting::getTimeMs();
- break;
-
- case KEY_RIGHT:
- if (event.KeyInput.Shift) {
- if (Text.size() > (u32)m_cursor_pos) {
- if (m_mark_begin == m_mark_end)
- new_mark_begin = m_cursor_pos;
-
- new_mark_end = m_cursor_pos + 1;
- }
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
-
- if (Text.size() > (u32)m_cursor_pos)
- m_cursor_pos++;
- m_blink_start_time = porting::getTimeMs();
- break;
- case KEY_UP:
- if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
- s32 lineNo = getLineFromPos(m_cursor_pos);
- s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
- if (lineNo > 0) {
- s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
- if ((s32)m_broken_text[lineNo - 1].size() < cp)
- m_cursor_pos = m_broken_text_positions[lineNo - 1] + core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
- else
- m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
- }
-
- if (event.KeyInput.Shift) {
- new_mark_begin = mb;
- new_mark_end = m_cursor_pos;
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
- } else {
- return false;
- }
- break;
- case KEY_DOWN:
- if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
- s32 lineNo = getLineFromPos(m_cursor_pos);
- s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
- if (lineNo < (s32)m_broken_text.size() - 1)
- {
- s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
- if ((s32)m_broken_text[lineNo + 1].size() < cp)
- m_cursor_pos = m_broken_text_positions[lineNo + 1] + core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
- else
- m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
- }
-
- if (event.KeyInput.Shift) {
- new_mark_begin = mb;
- new_mark_end = m_cursor_pos;
- } else {
- new_mark_begin = 0;
- new_mark_end = 0;
- }
-
- } else {
- return false;
- }
- break;
-
- case KEY_BACK:
- if (!isEnabled())
- break;
-
- if (Text.size()) {
- core::stringw s;
-
- if (m_mark_begin != m_mark_end) {
- // delete marked text
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- s = Text.subString(0, realmbgn);
- s.append(Text.subString(realmend, Text.size() - realmend));
- Text = s;
-
- m_cursor_pos = realmbgn;
- } else {
- // delete text behind cursor
- if (m_cursor_pos > 0)
- s = Text.subString(0, m_cursor_pos - 1);
- else
- s = L"";
- s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
- Text = s;
- --m_cursor_pos;
- }
-
- if (m_cursor_pos < 0)
- m_cursor_pos = 0;
- m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
- new_mark_begin = 0;
- new_mark_end = 0;
- text_changed = true;
- }
- break;
- case KEY_DELETE:
- if (!isEnabled())
- break;
-
- if (Text.size() != 0) {
- core::stringw s;
-
- if (m_mark_begin != m_mark_end) {
- // delete marked text
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- s = Text.subString(0, realmbgn);
- s.append(Text.subString(realmend, Text.size() - realmend));
- Text = s;
-
- m_cursor_pos = realmbgn;
- } else {
- // delete text before cursor
- s = Text.subString(0, m_cursor_pos);
- s.append(Text.subString(m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
- Text = s;
- }
-
- if (m_cursor_pos > (s32)Text.size())
- m_cursor_pos = (s32)Text.size();
-
- m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
- new_mark_begin = 0;
- new_mark_end = 0;
- text_changed = true;
- }
- break;
-
- case KEY_ESCAPE:
- case KEY_TAB:
- case KEY_SHIFT:
- case KEY_F1:
- case KEY_F2:
- case KEY_F3:
- case KEY_F4:
- case KEY_F5:
- case KEY_F6:
- case KEY_F7:
- case KEY_F8:
- case KEY_F9:
- case KEY_F10:
- case KEY_F11:
- case KEY_F12:
- case KEY_F13:
- case KEY_F14:
- case KEY_F15:
- case KEY_F16:
- case KEY_F17:
- case KEY_F18:
- case KEY_F19:
- case KEY_F20:
- case KEY_F21:
- case KEY_F22:
- case KEY_F23:
- case KEY_F24:
- // ignore these keys
- return false;
-
- default:
- inputChar(event.KeyInput.Char);
- return true;
- }
-
- // Set new text markers
- setTextMarkers(new_mark_begin, new_mark_end);
-
- // break the text if it has changed
- if (text_changed) {
- breakText();
- calculateScrollPos();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
- }
- else
- {
- calculateScrollPos();
- }
-
- return true;
-}
-
//! draws the element and its children
void GUIEditBoxWithScrollBar::draw()
@@ -839,131 +264,6 @@ void GUIEditBoxWithScrollBar::draw()
}
-//! Sets the new caption of this element.
-void GUIEditBoxWithScrollBar::setText(const wchar_t* text)
-{
- Text = text;
- if (u32(m_cursor_pos) > Text.size())
- m_cursor_pos = Text.size();
- m_hscroll_pos = 0;
- breakText();
-}
-
-
-//! Enables or disables automatic scrolling with cursor position
-//! \param enable: If set to true, the text will move around with the cursor position
-void GUIEditBoxWithScrollBar::setAutoScroll(bool enable)
-{
- m_autoscroll = enable;
-}
-
-
-//! Checks to see if automatic scrolling is enabled
-//! \return true if automatic scrolling is enabled, false if not
-bool GUIEditBoxWithScrollBar::isAutoScrollEnabled() const
-{
- return m_autoscroll;
-}
-
-
-//! Gets the area of the text in the edit box
-//! \return Returns the size in pixels of the text
-core::dimension2du GUIEditBoxWithScrollBar::getTextDimension()
-{
- core::rect<s32> ret;
-
- setTextRect(0);
- ret = m_current_text_rect;
-
- for (u32 i = 1; i < m_broken_text.size(); ++i) {
- setTextRect(i);
- ret.addInternalPoint(m_current_text_rect.UpperLeftCorner);
- ret.addInternalPoint(m_current_text_rect.LowerRightCorner);
- }
-
- return core::dimension2du(ret.getSize());
-}
-
-
-//! Sets the maximum amount of characters which may be entered in the box.
-//! \param max: Maximum amount of characters. If 0, the character amount is
-//! infinity.
-void GUIEditBoxWithScrollBar::setMax(u32 max)
-{
- m_max = max;
-
- if (Text.size() > m_max && m_max != 0)
- Text = Text.subString(0, m_max);
-}
-
-
-//! Returns maximum amount of characters, previously set by setMax();
-u32 GUIEditBoxWithScrollBar::getMax() const
-{
- return m_max;
-}
-
-
-bool GUIEditBoxWithScrollBar::processMouse(const SEvent& event)
-{
- switch (event.MouseInput.Event)
- {
- case irr::EMIE_LMOUSE_LEFT_UP:
- if (Environment->hasFocus(this)) {
- m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- if (m_mouse_marking) {
- setTextMarkers(m_mark_begin, m_cursor_pos);
- }
- m_mouse_marking = false;
- calculateScrollPos();
- return true;
- }
- break;
- case irr::EMIE_MOUSE_MOVED:
- {
- if (m_mouse_marking) {
- m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- setTextMarkers(m_mark_begin, m_cursor_pos);
- calculateScrollPos();
- return true;
- }
- }
- break;
- case EMIE_LMOUSE_PRESSED_DOWN:
-
- if (!Environment->hasFocus(this)) {
- m_blink_start_time = porting::getTimeMs();
- m_mouse_marking = true;
- m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- setTextMarkers(m_cursor_pos, m_cursor_pos);
- calculateScrollPos();
- return true;
- } else {
- if (!AbsoluteClippingRect.isPointInside(
- core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
- return false;
- } else {
- // move cursor
- m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-
- s32 newMarkBegin = m_mark_begin;
- if (!m_mouse_marking)
- newMarkBegin = m_cursor_pos;
-
- m_mouse_marking = true;
- setTextMarkers(newMarkBegin, m_cursor_pos);
- calculateScrollPos();
- return true;
- }
- }
- default:
- break;
- }
-
- return false;
-}
-
-
s32 GUIEditBoxWithScrollBar::getCursorPos(s32 x, s32 y)
{
IGUIFont* font = getActiveFont();
@@ -1028,7 +328,7 @@ void GUIEditBoxWithScrollBar::breakText()
s32 last_line_start = 0;
s32 size = Text.size();
s32 length = 0;
- s32 el_width = RelativeRect.getWidth() - 6;
+ s32 el_width = RelativeRect.getWidth() - m_scrollbar_width - 10;
wchar_t c;
for (s32 i = 0; i < size; ++i) {
@@ -1181,59 +481,6 @@ void GUIEditBoxWithScrollBar::setTextRect(s32 line)
m_current_text_rect += m_frame_rect.UpperLeftCorner;
}
-
-s32 GUIEditBoxWithScrollBar::getLineFromPos(s32 pos)
-{
- if (!m_word_wrap && !m_multiline)
- return 0;
-
- s32 i = 0;
- while (i < (s32)m_broken_text_positions.size()) {
- if (m_broken_text_positions[i] > pos)
- return i - 1;
- ++i;
- }
- return (s32)m_broken_text_positions.size() - 1;
-}
-
-
-void GUIEditBoxWithScrollBar::inputChar(wchar_t c)
-{
- if (!isEnabled())
- return;
-
- if (c != 0) {
- if (Text.size() < m_max || m_max == 0) {
- core::stringw s;
-
- if (m_mark_begin != m_mark_end) {
- // replace marked text
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- s = Text.subString(0, realmbgn);
- s.append(c);
- s.append(Text.subString(realmend, Text.size() - realmend));
- Text = s;
- m_cursor_pos = realmbgn + 1;
- } else {
- // add new character
- s = Text.subString(0, m_cursor_pos);
- s.append(c);
- s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
- Text = s;
- ++m_cursor_pos;
- }
-
- m_blink_start_time = porting::getTimeMs();
- setTextMarkers(0, 0);
- }
- }
- breakText();
- calculateScrollPos();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
-}
-
// calculate autoscroll
void GUIEditBoxWithScrollBar::calculateScrollPos()
{
@@ -1366,30 +613,6 @@ void GUIEditBoxWithScrollBar::calculateFrameRect()
updateVScrollBar();
}
-//! set text markers
-void GUIEditBoxWithScrollBar::setTextMarkers(s32 begin, s32 end)
-{
- if (begin != m_mark_begin || end != m_mark_end) {
- m_mark_begin = begin;
- m_mark_end = end;
- sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
- }
-}
-
-//! send some gui event to parent
-void GUIEditBoxWithScrollBar::sendGuiEvent(EGUI_EVENT_TYPE type)
-{
- if (Parent) {
- SEvent e;
- e.EventType = EET_GUI_EVENT;
- e.GUIEvent.Caller = this;
- e.GUIEvent.Element = 0;
- e.GUIEvent.EventType = type;
-
- Parent->OnEvent(e);
- }
-}
-
//! create a vertical scroll bar
void GUIEditBoxWithScrollBar::createVScrollBar()
{
@@ -1399,8 +622,6 @@ void GUIEditBoxWithScrollBar::createVScrollBar()
m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
- RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
-
irr::core::rect<s32> scrollbarrect = m_frame_rect;
scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
@@ -1411,61 +632,7 @@ void GUIEditBoxWithScrollBar::createVScrollBar()
m_vscrollbar->setLargeStep(1);
}
-void GUIEditBoxWithScrollBar::updateVScrollBar()
-{
- if (!m_vscrollbar) {
- return;
- }
- // OnScrollBarChanged(...)
- if (m_vscrollbar->getPos() != m_vscroll_pos) {
- s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
- m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
- m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
-
- s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
- 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
- m_vscroll_pos = m_vscrollbar->getPos();
- }
- }
-
- // check if a vertical scrollbar is needed ?
- if (getTextDimension().Height > (u32) m_frame_rect.getHeight()) {
- m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
-
- 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()) {
- m_vscrollbar->setVisible(true);
- }
- } else {
- if (m_vscrollbar->isVisible())
- {
- m_vscrollbar->setVisible(false);
- 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
-void GUIEditBoxWithScrollBar::setWritable(bool writable)
-{
- m_writable = writable;
-}
//! Change the background color
void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
@@ -1477,54 +644,21 @@ void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
//! Writes attributes of the element.
void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const
{
- // IGUIEditBox::serializeAttributes(out,options);
-
out->addBool("Border", m_border);
out->addBool("Background", m_background);
- out->addBool("OverrideColorEnabled", m_override_color_enabled);
- out->addColor("OverrideColor", m_override_color);
// out->addFont("OverrideFont", OverrideFont);
- out->addInt("MaxChars", m_max);
- out->addBool("WordWrap", m_word_wrap);
- out->addBool("MultiLine", m_multiline);
- out->addBool("AutoScroll", m_autoscroll);
- out->addBool("PasswordBox", m_passwordbox);
- core::stringw ch = L" ";
- ch[0] = m_passwordchar;
- out->addString("PasswordChar", ch.c_str());
- out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
- out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
- out->addBool("Writable", m_writable);
-
- IGUIEditBox::serializeAttributes(out, options);
+
+ GUIEditBox::serializeAttributes(out, options);
}
//! Reads attributes of the element
void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0)
{
- IGUIEditBox::deserializeAttributes(in, options);
+ GUIEditBox::deserializeAttributes(in, options);
setDrawBorder(in->getAttributeAsBool("Border"));
setDrawBackground(in->getAttributeAsBool("Background"));
- setOverrideColor(in->getAttributeAsColor("OverrideColor"));
- enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
- setMax(in->getAttributeAsInt("MaxChars"));
- setWordWrap(in->getAttributeAsBool("WordWrap"));
- setMultiLine(in->getAttributeAsBool("MultiLine"));
- setAutoScroll(in->getAttributeAsBool("AutoScroll"));
- core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
- if (!ch.size())
- setPasswordBox(in->getAttributeAsBool("PasswordBox"));
- else
- setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
- setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
- (EGUI_ALIGNMENT)in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
- // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
- setWritable(in->getAttributeAsBool("Writable"));
}
bool GUIEditBoxWithScrollBar::isDrawBackgroundEnabled() const { return false; }
diff --git a/src/gui/guiEditBoxWithScrollbar.h b/src/gui/guiEditBoxWithScrollbar.h
index 77538e2f7..3f7450dcb 100644
--- a/src/gui/guiEditBoxWithScrollbar.h
+++ b/src/gui/guiEditBoxWithScrollbar.h
@@ -5,15 +5,9 @@
#ifndef GUIEDITBOXWITHSCROLLBAR_HEADER
#define GUIEDITBOXWITHSCROLLBAR_HEADER
-#include "IGUIEditBox.h"
-#include "IOSOperator.h"
-#include "guiScrollBar.h"
-#include <vector>
+#include "guiEditBox.h"
-using namespace irr;
-using namespace irr::gui;
-
-class GUIEditBoxWithScrollBar : public IGUIEditBox
+class GUIEditBoxWithScrollBar : public GUIEditBox
{
public:
@@ -23,102 +17,17 @@ public:
bool writable = true, bool has_vscrollbar = true);
//! destructor
- virtual ~GUIEditBoxWithScrollBar();
-
- //! Sets another skin independent font.
- virtual void setOverrideFont(IGUIFont* font = 0);
-
- //! Gets the override font (if any)
- /** \return The override font (may be 0) */
- virtual IGUIFont* getOverrideFont() const;
-
- //! Get the font which is used right now for drawing
- /** Currently this is the override font when one is set and the
- font of the active skin otherwise */
- virtual IGUIFont* getActiveFont() const;
-
- //! Sets another color for the text.
- virtual void setOverrideColor(video::SColor color);
-
- //! Gets the override color
- virtual video::SColor getOverrideColor() const;
-
- //! Sets if the text should use the overide color or the
- //! color in the gui skin.
- virtual void enableOverrideColor(bool enable);
-
- //! Checks if an override color is enabled
- /** \return true if the override color is enabled, false otherwise */
- virtual bool isOverrideColorEnabled(void) const;
+ virtual ~GUIEditBoxWithScrollBar() {}
//! Sets whether to draw the background
virtual void setDrawBackground(bool draw);
- //! Turns the border on or off
- virtual void setDrawBorder(bool border);
-
- //! Enables or disables word wrap for using the edit box as multiline text editor.
- virtual void setWordWrap(bool enable);
-
- //! Checks if word wrap is enabled
- //! \return true if word wrap is enabled, false otherwise
- virtual bool isWordWrapEnabled() const;
-
- //! Enables or disables newlines.
- /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
- instead a newline character will be inserted. */
- virtual void setMultiLine(bool enable);
-
- //! Checks if multi line editing is enabled
- //! \return true if mult-line is enabled, false otherwise
- virtual bool isMultiLineEnabled() const;
-
- //! Enables or disables automatic scrolling with cursor position
- //! \param enable: If set to true, the text will move around with the cursor position
- virtual void setAutoScroll(bool enable);
-
- //! Checks to see if automatic scrolling is enabled
- //! \return true if automatic scrolling is enabled, false if not
- virtual bool isAutoScrollEnabled() const;
-
- //! Gets the size area of the text in the edit box
- //! \return Returns the size in pixels of the text
- virtual core::dimension2du getTextDimension();
-
- //! Sets text justification
- virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
-
- //! called if an event happened.
- virtual bool OnEvent(const SEvent& event);
-
//! draws the element and its children
virtual void draw();
- //! Sets the new caption of this element.
- virtual void setText(const wchar_t* text);
-
- //! Sets the maximum amount of characters which may be entered in the box.
- //! \param max: Maximum amount of characters. If 0, the character amount is
- //! infinity.
- virtual void setMax(u32 max);
-
- //! Returns maximum amount of characters, previously set by setMax();
- virtual u32 getMax() const;
-
- //! Sets whether the edit box is a password box. Setting this to true will
- /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
- \param passwordBox: true to enable password, false to disable
- \param passwordChar: the character that is displayed instead of letters */
- virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
-
- //! Returns true if the edit box is currently a password box.
- virtual bool isPasswordBox() const;
-
//! Updates the absolute position, splits text if required
virtual void updateAbsolutePosition();
- virtual void setWritable(bool writable);
-
//! Change the background color
virtual void setBackgroundColor(const video::SColor &bg_color);
@@ -137,58 +46,19 @@ public:
protected:
//! Breaks the single text line.
- void breakText();
+ virtual void breakText();
//! sets the area of the given line
- void setTextRect(s32 line);
- //! returns the line number that the cursor is on
- s32 getLineFromPos(s32 pos);
- //! adds a letter to the edit box
- void inputChar(wchar_t c);
+ virtual void setTextRect(s32 line);
//! calculates the current scroll position
void calculateScrollPos();
//! calculated the FrameRect
void calculateFrameRect();
- //! send some gui event to parent
- void sendGuiEvent(EGUI_EVENT_TYPE type);
- //! set text markers
- void setTextMarkers(s32 begin, s32 end);
//! create a Vertical ScrollBar
void createVScrollBar();
- //! update the vertical scrollBar (visibilty & position)
- void updateVScrollBar();
- bool processKey(const SEvent& event);
- bool processMouse(const SEvent& event);
s32 getCursorPos(s32 x, s32 y);
- bool m_mouse_marking;
- bool m_border;
bool m_background;
- bool m_override_color_enabled;
- s32 m_mark_begin;
- s32 m_mark_end;
-
- video::SColor m_override_color;
- gui::IGUIFont *m_override_font, *m_last_break_font;
- IOSOperator* m_operator;
-
- u32 m_blink_start_time;
- s32 m_cursor_pos;
- s32 m_hscroll_pos, m_vscroll_pos; // scroll position in characters
- u32 m_max;
-
- bool m_word_wrap, m_multiline, m_autoscroll, m_passwordbox;
- wchar_t m_passwordchar;
- EGUI_ALIGNMENT m_halign, m_valign;
-
- std::vector<core::stringw> m_broken_text;
- std::vector<s32> m_broken_text_positions;
-
- core::rect<s32> m_current_text_rect, m_frame_rect; // temporary values
-
- u32 m_scrollbar_width;
- GUIScrollBar *m_vscrollbar;
- bool m_writable;
bool m_bg_color_used;
video::SColor m_bg_color;
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index b40707d01..93463ad70 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -75,8 +75,6 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
if (name.empty())
return NULL;
- m_to_delete.insert(name);
-
#if ENABLE_GLES
video::ITexture *retval = m_driver->findTexture(name.c_str());
if (retval)
@@ -88,6 +86,7 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
image = Align2Npot2(image, m_driver);
retval = m_driver->addTexture(name.c_str(), image);
+ m_to_delete.insert(name);
image->drop();
return retval;
#else
@@ -170,6 +169,7 @@ GUIEngine::GUIEngine(JoystickController *joystick,
m_menumanager,
NULL /* &client */,
m_texture_source,
+ m_sound_manager,
m_formspecgui,
m_buttonhandler,
"",
@@ -297,10 +297,14 @@ void GUIEngine::run()
driver->endScene();
+ IrrlichtDevice *device = RenderingEngine::get_raw_device();
+ u32 frametime_min = 1000 / (device->isWindowFocused()
+ ? g_settings->getFloat("fps_max")
+ : g_settings->getFloat("fps_max_unfocused"));
if (m_clouds_enabled)
- cloudPostProcess();
+ cloudPostProcess(frametime_min, device);
else
- sleep_ms(25);
+ sleep_ms(frametime_min);
m_script->step();
@@ -367,9 +371,8 @@ void GUIEngine::cloudPreProcess()
}
/******************************************************************************/
-void GUIEngine::cloudPostProcess()
+void GUIEngine::cloudPostProcess(u32 frametime_min, IrrlichtDevice *device)
{
- float fps_max = g_settings->getFloat("pause_fps_max");
// Time of frame without fps limit
u32 busytime_u32;
@@ -380,12 +383,10 @@ void GUIEngine::cloudPostProcess()
else
busytime_u32 = 0;
- // FPS limiter
- u32 frametime_min = 1000./fps_max;
-
+ // FPS limit
if (busytime_u32 < frametime_min) {
u32 sleeptime = frametime_min - busytime_u32;
- RenderingEngine::get_raw_device()->sleep(sleeptime);
+ device->sleep(sleeptime);
}
}
@@ -484,8 +485,6 @@ void GUIEngine::drawHeader(video::IVideoDriver *driver)
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
((free_space/2)-splashsize.Y/2)+10);
- video::SColor bgcolor(255,50,50,50);
-
draw2DImageFilterScaled(driver, texture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h
index e55531bbc..eef1ad8aa 100644
--- a/src/gui/guiEngine.h
+++ b/src/gui/guiEngine.h
@@ -23,29 +23,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
/* Includes */
/******************************************************************************/
#include "irrlichttypes.h"
-#include "modalMenu.h"
#include "guiFormSpecMenu.h"
#include "client/sound.h"
#include "client/tile.h"
#include "util/enriched_string.h"
/******************************************************************************/
-/* Typedefs and macros */
+/* Structs and macros */
/******************************************************************************/
/** texture layer ids */
-typedef enum {
+enum texture_layer {
TEX_LAYER_BACKGROUND = 0,
TEX_LAYER_OVERLAY,
TEX_LAYER_HEADER,
TEX_LAYER_FOOTER,
TEX_LAYER_MAX
-} texture_layer;
+};
-typedef struct {
+struct image_definition {
video::ITexture *texture = nullptr;
bool tile;
unsigned int minsize;
-} image_definition;
+};
/******************************************************************************/
/* forward declarations */
@@ -278,7 +277,7 @@ private:
/** do preprocessing for cloud subsystem */
void cloudPreProcess();
/** do postprocessing for cloud subsystem */
- void cloudPostProcess();
+ void cloudPostProcess(u32 frametime_min, IrrlichtDevice *device);
/** internam data required for drawing clouds */
struct clouddata {
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 37edf3c4b..5aa6dc9ae 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -19,13 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cstdlib>
+#include <cmath>
#include <algorithm>
#include <iterator>
#include <limits>
#include <sstream>
#include "guiFormSpecMenu.h"
-#include "guiScrollBar.h"
-#include "guiTable.h"
#include "constants.h"
#include "gamedef.h"
#include "client/keycode.h"
@@ -50,6 +49,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h"
#include "client/client.h"
#include "client/fontengine.h"
+#include "client/sound.h"
#include "util/hex.h"
#include "util/numeric.h"
#include "util/string.h" // for parseColorString()
@@ -64,15 +64,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiEditBoxWithScrollbar.h"
#include "guiInventoryList.h"
#include "guiItemImage.h"
-#include "guiScrollBar.h"
#include "guiScrollContainer.h"
-#include "guiTable.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
+#include "guiScene.h"
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
- errorstream<< "Invalid pos for element " << a << "specified: \"" \
+ errorstream<< "Invalid pos for element " << a << " specified: \"" \
<< parts[b] << "\"" << std::endl; \
return; \
}
@@ -80,7 +79,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MY_CHECKGEOM(a,b) \
if (v_geom.size() != 2) { \
errorstream<< "Invalid geometry for element " << a << \
- "specified: \"" << parts[b] << "\"" << std::endl; \
+ " specified: \"" << parts[b] << "\"" << std::endl; \
return; \
}
/*
@@ -98,30 +97,24 @@ 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,
- const std::string &formspecPrepend,
- bool remap_dbl_click):
- GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
+ Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager,
+ IFormSource *fsrc, TextDest *tdst,
+ const std::string &formspecPrepend, bool remap_dbl_click):
+ GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click),
m_invmgr(client),
m_tsrc(tsrc),
+ m_sound_manager(sound_manager),
m_client(client),
m_formspec_prepend(formspecPrepend),
m_form_src(fsrc),
m_text_dst(tdst),
- m_joystick(joystick),
- m_remap_dbl_click(remap_dbl_click)
+ m_joystick(joystick)
{
current_keys_pending.key_down = false;
current_keys_pending.key_up = false;
current_keys_pending.key_enter = false;
current_keys_pending.key_escape = false;
- m_doubleclickdetect[0].time = 0;
- m_doubleclickdetect[1].time = 0;
-
- m_doubleclickdetect[0].pos = v2s32(0, 0);
- m_doubleclickdetect[1].pos = v2s32(0, 0);
-
m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname");
}
@@ -154,11 +147,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
- const std::string &formspecPrepend)
+ const std::string &formspecPrepend, ISoundManager *sound_manager)
{
if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
- client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend);
+ client, client->getTextureSource(), sound_manager, fs_src,
+ txt_dest, formspecPrepend);
cur_formspec->doPause = false;
/*
@@ -504,19 +498,39 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element)
3
);
- v2f32 slot_spacing = data->real_coordinates ?
- v2f32(imgsize.X * 1.25f, imgsize.Y * 1.25f) : spacing;
+ auto style = getDefaultStyleForElement("list", spec.fname);
+
+ v2f32 slot_scale = style.getVector2f(StyleSpec::SIZE, v2f32(0, 0));
+ v2f32 slot_size(
+ slot_scale.X <= 0 ? imgsize.X : std::max<f32>(slot_scale.X * imgsize.X, 1),
+ slot_scale.Y <= 0 ? imgsize.Y : std::max<f32>(slot_scale.Y * imgsize.Y, 1)
+ );
+
+ v2f32 slot_spacing = style.getVector2f(StyleSpec::SPACING, v2f32(-1, -1));
+ v2f32 default_spacing = data->real_coordinates ?
+ v2f32(imgsize.X * 0.25f, imgsize.Y * 0.25f) :
+ v2f32(spacing.X - imgsize.X, spacing.Y - imgsize.Y);
+
+ slot_spacing.X = slot_spacing.X < 0 ? default_spacing.X :
+ imgsize.X * slot_spacing.X;
+ slot_spacing.Y = slot_spacing.Y < 0 ? default_spacing.Y :
+ imgsize.Y * slot_spacing.Y;
+
+ slot_spacing += slot_size;
- v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos)
- : getElementBasePos(&v_pos);
+ v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos) :
+ getElementBasePos(&v_pos);
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
- pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X,
- pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y);
+ pos.X + (geom.X - 1) * slot_spacing.X + slot_size.X,
+ pos.Y + (geom.Y - 1) * slot_spacing.Y + slot_size.Y);
GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent,
- spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize,
- slot_spacing, this, data->inventorylist_options, m_font);
+ spec.fid, rect, m_invmgr, loc, listname, geom, start_i,
+ v2s32(slot_size.X, slot_size.Y), slot_spacing, this,
+ data->inventorylist_options, m_font);
+
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
m_inventorylists.push_back(e);
m_fields.push_back(spec);
@@ -625,9 +639,12 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getDefaultStyleForElement("checkbox", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -703,6 +720,10 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size);
+ if (spec.fname == m_focused_element) {
+ Environment->setFocus(e);
+ }
+
m_scrollbars.emplace_back(spec,e);
m_fields.push_back(spec);
return;
@@ -907,7 +928,7 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el
core::rect<s32> rect = core::rect<s32>(pos, pos + geom);
- GUIAnimatedImage *e = new GUIAnimatedImage(Environment, this, spec.fid,
+ GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid,
rect, texture_name, frame_count, frame_duration, m_tsrc);
if (parts.size() >= 7)
@@ -1027,9 +1048,12 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
+
+ spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
e->setStyles(style);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1218,7 +1242,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
rect, m_tsrc);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1233,6 +1257,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
auto style = getDefaultStyleForElement("table", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideFont(style.getFont());
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
@@ -1295,7 +1320,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
rect, m_tsrc);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1310,6 +1335,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
auto style = getDefaultStyleForElement("textlist", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideFont(style.getFont());
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
@@ -1318,19 +1344,20 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-
void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
- if ((parts.size() == 5) ||
- ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+ if (parts.size() == 5 || parts.size() == 6 ||
+ (parts.size() > 6 && m_formspec_version > FORMSPEC_API_VERSION))
{
- std::vector<std::string> v_pos = split(parts[0],',');
+ std::vector<std::string> v_pos = split(parts[0], ',');
std::string name = parts[2];
- std::vector<std::string> items = split(parts[3],',');
- std::string str_initial_selection;
- str_initial_selection = parts[4];
+ std::vector<std::string> items = split(parts[3], ',');
+ std::string str_initial_selection = parts[4];
+
+ if (parts.size() >= 6 && is_yes(parts[5]))
+ m_dropdown_index_event[name] = true;
MY_CHECKPOS("dropdown",0);
@@ -1372,7 +1399,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent,
spec.fid);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1385,6 +1412,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
e->setSelected(stoi(str_initial_selection)-1);
auto style = getDefaultStyleForElement("dropdown", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
m_fields.push_back(spec);
@@ -1397,8 +1427,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
return;
}
- errorstream << "Invalid dropdown element(" << parts.size() << "): '"
- << element << "'" << std::endl;
+ errorstream << "Invalid dropdown element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
@@ -1414,8 +1444,8 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
{
std::vector<std::string> parts = split(element,';');
- if ((parts.size() == 4) ||
- ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+ if (parts.size() == 4 ||
+ (parts.size() > 4 && m_formspec_version > FORMSPEC_API_VERSION))
{
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
@@ -1459,7 +1489,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
data->current_parent, spec.fid);
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
@@ -1477,6 +1507,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+ e->setOverrideFont(style.getFont());
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
@@ -1536,7 +1567,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
if (e) {
- if (is_editable && spec.fname == data->focused_fieldname)
+ if (is_editable && spec.fname == m_focused_element)
Environment->setFocus(e);
if (is_multiline) {
@@ -1560,6 +1591,7 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
if (style.get(StyleSpec::BGCOLOR, "") == "transparent") {
e->setDrawBackground(false);
}
+ e->setOverrideFont(style.getFont());
e->drop();
}
@@ -1743,12 +1775,16 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen
FieldSpec spec(
name,
- utf8_to_wide(unescape_string(text)),
+ translate_string(utf8_to_wide(unescape_string(text))),
L"",
258 + m_fields.size()
);
spec.ftype = f_HyperText;
+
+ auto style = getDefaultStyleForElement("hypertext", spec.fname);
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment,
data->current_parent, spec.fid, rect, m_client, m_tsrc);
e->drop();
@@ -1773,6 +1809,11 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
std::vector<std::string> lines = split(text, '\n');
+ auto style = getDefaultStyleForElement("label", "");
+ gui::IGUIFont *font = style.getFont();
+ if (!font)
+ font = m_font;
+
for (unsigned int i = 0; i != lines.size(); i++) {
std::wstring wlabel_colors = translate_string(
utf8_to_wide(unescape_string(lines[i])));
@@ -1794,7 +1835,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
rect = core::rect<s32>(
pos.X, pos.Y,
- pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
+ pos.X + font->getDimension(wlabel_plain.c_str()).Width,
pos.Y + imgsize.Y);
} else {
@@ -1816,7 +1857,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
rect = core::rect<s32>(
pos.X, pos.Y - m_btn_height,
- pos.X + m_font->getDimension(wlabel_plain.c_str()).Width,
+ pos.X + font->getDimension(wlabel_plain.c_str()).Width,
pos.Y + m_btn_height);
}
@@ -1832,9 +1873,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
- auto style = getDefaultStyleForElement("label", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+ e->setOverrideFont(font);
m_fields.push_back(spec);
@@ -1862,6 +1903,11 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
MY_CHECKPOS("vertlabel",1);
+ auto style = getDefaultStyleForElement("vertlabel", "", "label");
+ gui::IGUIFont *font = style.getFont();
+ if (!font)
+ font = m_font;
+
v2s32 pos;
core::rect<s32> rect;
@@ -1875,7 +1921,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
// 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) *
+ pos.Y + font_line_height(font) *
(text.length() + 1));
} else {
@@ -1887,7 +1933,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
rect = core::rect<s32>(
pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
pos.X+15, pos.Y +
- font_line_height(m_font) *
+ font_line_height(font) *
(text.length() + 1) +
((imgsize.Y/2) - m_btn_height));
}
@@ -1912,9 +1958,9 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
rect, false, false, data->current_parent, spec.fid);
e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
- auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label");
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+ e->setOverrideFont(font);
m_fields.push_back(spec);
@@ -1985,12 +2031,14 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
data->current_parent, spec.fid, spec.flabel.c_str());
- if (spec.fname == data->focused_fieldname) {
+ if (spec.fname == m_focused_element) {
Environment->setFocus(e);
}
auto style = getStyleForElement("image_button", spec.fname);
+ spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
// Override style properties with values specified directly in the element
if (!image_name.empty())
style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name);
@@ -2098,11 +2146,10 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
e->setTabHeight(geom.Y);
- if (spec.fname == data->focused_fieldname) {
- Environment->setFocus(e);
- }
-
auto style = getDefaultStyleForElement("tabheader", name);
+
+ spec.sound = style.get(StyleSpec::Property::SOUND, "");
+
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
for (const std::string &button : buttons) {
@@ -2191,9 +2238,12 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
item_name, m_client);
auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
+
+ spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, "");
+
e_btn->setStyles(style);
- if (spec_btn.fname == data->focused_fieldname) {
+ if (spec_btn.fname == m_focused_element) {
Environment->setFocus(e_btn);
}
@@ -2208,16 +2258,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
if ((parts.size() == 3) ||
((parts.size() > 3) && (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::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
- MY_CHECKPOS("box",0);
- MY_CHECKGEOM("box",1);
+ MY_CHECKPOS("box", 0);
+ MY_CHECKGEOM("box", 1);
v2s32 pos;
v2s32 geom;
@@ -2231,36 +2281,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
geom.Y = stof(v_geom[1]) * spacing.Y;
}
- video::SColor tmp_color;
-
- if (parseColorString(parts[2], tmp_color, false, 0x8C)) {
- FieldSpec spec(
- "",
- L"",
- L"",
- 258 + m_fields.size(),
- -2
- );
- spec.ftype = f_Box;
+ FieldSpec spec(
+ "",
+ L"",
+ L"",
+ 258 + m_fields.size(),
+ -2
+ );
+ spec.ftype = f_Box;
- core::rect<s32> rect(pos, pos + geom);
+ auto style = getDefaultStyleForElement("box", spec.fname);
- GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid,
- rect, tmp_color);
+ video::SColor tmp_color;
+ std::array<video::SColor, 4> colors;
+ std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0};
+ std::array<s32, 4> borderwidths = {0, 0, 0, 0};
- auto style = getDefaultStyleForElement("box", spec.fname);
- e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ if (parseColorString(parts[2], tmp_color, true, 0x8C)) {
+ colors = {tmp_color, tmp_color, tmp_color, tmp_color};
+ } else {
+ colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0});
+ bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS,
+ {0x0, 0x0, 0x0, 0x0});
+ borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0});
+ }
- e->drop();
+ core::rect<s32> rect(pos, pos + geom);
- m_fields.push_back(spec);
+ GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect,
+ colors, bordercolors, borderwidths);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
+ e->drop();
- } else {
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
- }
+ m_fields.push_back(spec);
return;
}
- errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid Box element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
@@ -2664,6 +2721,118 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b
return true;
}
+void GUIFormSpecMenu::parseSetFocus(const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() <= 2 ||
+ (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION))
+ {
+ if (m_is_form_regenerated)
+ return; // Never focus on resizing
+
+ bool force_focus = parts.size() >= 2 && is_yes(parts[1]);
+ if (force_focus || m_text_dst->m_formname != m_last_formname)
+ setFocus(parts[0]);
+
+ return;
+ }
+
+ errorstream << "Invalid set_focus element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 5 || (parts.size() > 9 &&
+ m_formspec_version <= FORMSPEC_API_VERSION)) {
+ errorstream << "Invalid model element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return;
+ }
+
+ // Avoid length checks by resizing
+ if (parts.size() < 9)
+ parts.resize(9);
+
+ 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]);
+ std::string meshstr = unescape_string(parts[3]);
+ std::vector<std::string> textures = split(parts[4], ',');
+ std::vector<std::string> vec_rot = split(parts[5], ',');
+ bool inf_rotation = is_yes(parts[6]);
+ bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true
+ std::vector<std::string> frame_loop = split(parts[8], ',');
+
+ MY_CHECKPOS("model", 0);
+ MY_CHECKGEOM("model", 1);
+
+ v2s32 pos;
+ v2s32 geom;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(&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 model without a size[] element" << std::endl;
+
+ scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr);
+
+ if (!mesh) {
+ errorstream << "Invalid model element: Unable to load mesh:"
+ << std::endl << "\t" << meshstr << std::endl;
+ return;
+ }
+
+ FieldSpec spec(
+ name,
+ L"",
+ L"",
+ 258 + m_fields.size()
+ );
+
+ core::rect<s32> rect(pos, pos + geom);
+
+ GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(),
+ data->current_parent, rect, spec.fid);
+
+ auto meshnode = e->setMesh(mesh);
+
+ for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i)
+ e->setTexture(i, m_tsrc->getTexture(unescape_string(textures[i])));
+
+ if (vec_rot.size() >= 2)
+ e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1])));
+
+ e->enableContinuousRotation(inf_rotation);
+ e->enableMouseControl(mousectrl);
+
+ s32 frame_loop_begin = 0;
+ s32 frame_loop_end = 0x7FFFFFFF;
+
+ if (frame_loop.size() == 2) {
+ frame_loop_begin = stoi(frame_loop[0]);
+ frame_loop_end = stoi(frame_loop[1]);
+ }
+
+ e->setFrameLoop(frame_loop_begin, frame_loop_end);
+
+ auto style = getStyleForElement("model", spec.fname);
+ e->setStyles(style);
+ e->drop();
+
+ m_fields.push_back(spec);
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
@@ -2855,6 +3024,16 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "set_focus") {
+ parseSetFocus(description);
+ return;
+ }
+
+ if (type == "model") {
+ parseModel(data, description);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
@@ -2862,36 +3041,38 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
- /* useless to regenerate without a screensize */
+ // Useless to regenerate without a screensize
if ((screensize.X <= 0) || (screensize.Y <= 0)) {
return;
}
parserData mydata;
- //preserve tables
- for (auto &m_table : m_tables) {
- std::string tablename = m_table.first.fname;
- GUITable *table = m_table.second;
- mydata.table_dyndata[tablename] = table->getDynamicData();
- }
-
- //set focus
- if (!m_focused_element.empty())
- mydata.focused_fieldname = m_focused_element;
+ // Preserve stuff only on same form, not on a new form.
+ if (m_text_dst->m_formname == m_last_formname) {
+ // Preserve tables/textlists
+ for (auto &m_table : m_tables) {
+ std::string tablename = m_table.first.fname;
+ GUITable *table = m_table.second;
+ mydata.table_dyndata[tablename] = table->getDynamicData();
+ }
- //preserve focus
- gui::IGUIElement *focused_element = Environment->getFocus();
- if (focused_element && focused_element->getParent() == this) {
- s32 focused_id = focused_element->getID();
- if (focused_id > 257) {
- for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
- if (field.fid == focused_id) {
- mydata.focused_fieldname = field.fname;
- break;
+ // Preserve focus
+ gui::IGUIElement *focused_element = Environment->getFocus();
+ if (focused_element && focused_element->getParent() == this) {
+ s32 focused_id = focused_element->getID();
+ if (focused_id > 257) {
+ for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
+ if (field.fid == focused_id) {
+ m_focused_element = field.fname;
+ break;
+ }
}
}
}
+ } else {
+ // Don't keep old focus value
+ m_focused_element = "";
}
// Remove children
@@ -2940,6 +3121,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
theme_by_name.clear();
theme_by_type.clear();
m_clickthrough_elements.clear();
+ field_close_on_enter.clear();
+ m_dropdown_index_event.clear();
m_bgnonfullscreen = true;
m_bgfullscreen = false;
@@ -3079,42 +3262,42 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// and default scaling (1.00).
use_imgsize = 0.5555 * screen_dpi * gui_scaling;
} else {
- // In variable-size mode, we prefer to make the
- // inventory image size 1/15 of screen height,
- // multiplied by the gui_scaling config parameter.
- // If the preferred size won't fit the whole
- // form on the screen, either horizontally or
- // vertically, then we scale it down to fit.
- // (The magic numbers in the computation of what
- // fits arise from the scaling factors in the
- // following stanza, including the form border,
- // help text space, and 0.1 inventory slot spare.)
- // However, a minimum size is also set, that
- // the image size can't be less than 0.3 inch
- // multiplied by gui_scaling, even if this means
- // the form doesn't fit the screen.
+ // Variables for the maximum imgsize that can fit in the screen.
+ double fitx_imgsize;
+ double fity_imgsize;
+
+ // Pad the screensize with 5% of the screensize on all sides to ensure
+ // that even the largest formspecs don't touch the screen borders.
+ v2f padded_screensize(
+ mydata.screensize.X * 0.9f,
+ mydata.screensize.Y * 0.9f
+ );
+
+ if (mydata.real_coordinates) {
+ fitx_imgsize = padded_screensize.X / mydata.invsize.X;
+ fity_imgsize = padded_screensize.Y / mydata.invsize.Y;
+ } else {
+ // The maximum imgsize in the old coordinate system also needs to
+ // factor in padding and spacing along with 0.1 inventory slot spare
+ // and help text space, hence the magic numbers.
+ fitx_imgsize = padded_screensize.X /
+ ((5.0 / 4.0) * (0.5 + mydata.invsize.X));
+ fity_imgsize = padded_screensize.Y /
+ ((15.0 / 13.0) * (0.85 + mydata.invsize.Y));
+ }
+
#ifdef __ANDROID__
- // For mobile devices these magic numbers are
- // different and forms should always use the
- // maximum screen space available.
- double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling;
- double fitx_imgsize = mydata.screensize.X /
- ((12.0 / 8.0) * (0.5 + mydata.invsize.X));
- double fity_imgsize = mydata.screensize.Y /
- ((15.0 / 11.0) * (0.85 + mydata.invsize.Y));
- use_imgsize = MYMIN(prefer_imgsize,
- MYMIN(fitx_imgsize, fity_imgsize));
+ // In Android, the preferred imgsize should be larger to accommodate the
+ // smaller screensize.
+ double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling;
#else
- double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling;
- double fitx_imgsize = mydata.screensize.X /
- ((5.0 / 4.0) * (0.5 + mydata.invsize.X));
- double fity_imgsize = mydata.screensize.Y /
- ((15.0 / 13.0) * (0.85 * mydata.invsize.Y));
- double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
- double min_imgsize = 0.3 * screen_dpi * gui_scaling;
- use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
- MYMIN(fitx_imgsize, fity_imgsize)));
+ // Desktop computers have more space, so try to fit 15 coordinates.
+ double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling;
#endif
+ // Try to use the preferred imgsize, but if that's bigger than the maximum
+ // size, use the maximum size.
+ use_imgsize = std::min(prefer_imgsize,
+ std::min(fitx_imgsize, fity_imgsize));
}
// Everything else is scaled in proportion to the
@@ -3250,8 +3433,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
}
}
- //set initial focus if parser didn't set it
- focused_element = Environment->getFocus();
+ // Set initial focus if parser didn't set it
+ gui::IGUIElement *focused_element = Environment->getFocus();
if (!focused_element
|| !isMyChild(focused_element)
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
@@ -3262,6 +3445,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// legacy sorting
if (m_formspec_version < 3)
legacySortElements(legacy_sort_start);
+
+ // Formname and regeneration setting
+ if (!m_is_form_regenerated) {
+ // Only set previous form name if we purposefully showed a new formspec
+ m_last_formname = m_text_dst->m_formname;
+ m_is_form_regenerated = true;
+ }
}
void GUIFormSpecMenu::legacySortElements(core::list<IGUIElement *>::Iterator from)
@@ -3336,8 +3526,6 @@ bool GUIFormSpecMenu::getAndroidUIInput()
GUIInventoryList::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
{
- core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
-
for (const GUIInventoryList *e : m_inventorylists) {
s32 item_index = e->getItemIndexAtPos(p);
if (item_index != -1)
@@ -3379,6 +3567,7 @@ void GUIFormSpecMenu::drawMenu()
const std::string &newform = m_form_src->getForm();
if (newform != m_formspec_string) {
m_formspec_string = newform;
+ m_is_form_regenerated = false;
regenerateGui(m_screensize_old);
}
}
@@ -3435,10 +3624,14 @@ void GUIFormSpecMenu::drawMenu()
e->setVisible(true);
/*
- Call base class
- (This is where all the drawing happens.)
+ This is where all the drawing happens.
*/
- gui::IGUIElement::draw();
+ core::list<IGUIElement*>::Iterator it = Children.begin();
+ for (; it != Children.end(); ++it)
+ if ((*it)->isNotClipped() ||
+ AbsoluteClippingRect.isRectCollided(
+ (*it)->getAbsolutePosition()))
+ (*it)->draw();
for (gui::IGUIElement *e : m_clickthrough_elements)
e->setVisible(false);
@@ -3544,7 +3737,8 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
{
EnrichedString ntext(text);
ntext.setDefaultColor(color);
- ntext.setBackground(bgcolor);
+ if (!ntext.hasBackground())
+ ntext.setBackground(bgcolor);
setStaticText(m_tooltip_element, ntext);
@@ -3662,7 +3856,7 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
return ItemStack();
}
-void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
+void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)
{
if(m_text_dst)
{
@@ -3727,10 +3921,14 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
}
s32 selected = e->getSelected();
if (selected >= 0) {
- std::vector<std::string> *dropdown_values =
- getDropDownValues(s.fname);
- if (dropdown_values && selected < (s32)dropdown_values->size()) {
- fields[name] = (*dropdown_values)[selected];
+ if (m_dropdown_index_event.find(s.fname) !=
+ m_dropdown_index_event.end()) {
+ fields[name] = std::to_string(selected + 1);
+ } else {
+ std::vector<std::string> *dropdown_values =
+ getDropDownValues(s.fname);
+ if (dropdown_values && selected < (s32)dropdown_values->size())
+ fields[name] = (*dropdown_values)[selected];
}
}
} else if (s.ftype == f_TabHeader) {
@@ -3800,17 +3998,6 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
}
}
-static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
-{
- while (tocheck) {
- if (tocheck == parent) {
- return true;
- }
- tocheck = tocheck->getParent();
- }
- return false;
-}
-
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
{
// The IGUITabControl renders visually using the skin's selected
@@ -3871,22 +4058,6 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
}
}
- if (event.EventType == EET_MOUSE_INPUT_EVENT) {
- s32 x = event.MouseInput.X;
- s32 y = event.MouseInput.Y;
- gui::IGUIElement *hovered =
- Environment->getRootGUIElement()->getElementFromPoint(
- core::position2d<s32>(x, y));
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- m_old_tooltip_id = -1;
- }
- if (!isChild(hovered, this)) {
- if (DoubleClickDetection(event)) {
- return true;
- }
- }
- }
-
if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
/* TODO add a check like:
if (event.JoystickEvent != joystick_we_listen_for)
@@ -3909,64 +4080,6 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
return GUIModalMenu::preprocessEvent(event);
}
-/******************************************************************************/
-bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
-{
- /* The following code is for capturing double-clicks of the mouse button
- * and translating the double-click into an EET_KEY_INPUT_EVENT event
- * -- which closes the form -- under some circumstances.
- *
- * There have been many github issues reporting this as a bug even though it
- * was an intended feature. For this reason, remapping the double-click as
- * an ESC must be explicitly set when creating this class via the
- * /p remap_dbl_click parameter of the constructor.
- */
-
- if (!m_remap_dbl_click)
- return false;
-
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
- m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
-
- m_doubleclickdetect[1].pos = m_pointer;
- m_doubleclickdetect[1].time = porting::getTimeMs();
- }
- else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
- if (delta > 400) {
- return false;
- }
-
- double squaredistance =
- m_doubleclickdetect[0].pos
- .getDistanceFromSQ(m_doubleclickdetect[1].pos);
-
- if (squaredistance > (30*30)) {
- return false;
- }
-
- SEvent* translated = new SEvent();
- assert(translated != 0);
- //translate doubleclick to escape
- memset(translated, 0, sizeof(SEvent));
- translated->EventType = irr::EET_KEY_INPUT_EVENT;
- translated->KeyInput.Key = KEY_ESCAPE;
- translated->KeyInput.Control = false;
- translated->KeyInput.Shift = false;
- translated->KeyInput.PressedDown = true;
- translated->KeyInput.Char = 0;
- OnEvent(*translated);
-
- // no need to send the key up event as we're already deleted
- // and no one else did notice this event
- delete translated;
- return true;
- }
-
- return false;
-}
-
void GUIFormSpecMenu::tryClose()
{
if (m_allowclose) {
@@ -4429,6 +4542,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
if ((s.ftype == f_TabHeader) &&
(s.fid == event.GUIEvent.Caller->getID())) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput();
s.send = false;
@@ -4472,6 +4587,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
continue;
if (s.ftype == f_Button || s.ftype == f_CheckBox) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
+
s.send = true;
if (s.is_exit) {
if (m_allowclose) {
@@ -4494,6 +4612,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s2.send = false;
}
}
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput(quit_mode_no);
@@ -4510,6 +4630,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
acceptInput(quit_mode_no);
s.fdefault = L"";
} else if (s.ftype == f_Unknown || s.ftype == f_HyperText) {
+ if (!s.sound.empty() && m_sound_manager)
+ m_sound_manager->playSound(s.sound, false, 1.0f);
s.send = true;
acceptInput();
s.send = false;
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index 28088416d..d658aba7b 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventorymanager.h"
#include "modalMenu.h"
#include "guiInventoryList.h"
+#include "guiScrollBar.h"
#include "guiTable.h"
#include "network/networkprotocol.h"
#include "client/joystick_controller.h"
@@ -37,11 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class InventoryManager;
class ISimpleTextureSource;
class Client;
-class GUIScrollBar;
-class TexturePool;
class GUIScrollContainer;
+class ISoundManager;
-typedef enum {
+enum FormspecFieldType {
f_Button,
f_Table,
f_TabHeader,
@@ -53,13 +53,13 @@ typedef enum {
f_HyperText,
f_AnimatedImage,
f_Unknown
-} FormspecFieldType;
+};
-typedef enum {
+enum FormspecQuitMode {
quit_mode_no,
quit_mode_accept,
quit_mode_cancel
-} FormspecQuitMode;
+};
struct TextDest
{
@@ -128,6 +128,7 @@ class GUIFormSpecMenu : public GUIModalMenu
int priority;
core::rect<s32> rect;
gui::ECURSOR_ICON fcursor_icon;
+ std::string sound;
};
struct TooltipSpec
@@ -152,6 +153,7 @@ public:
IMenuManager *menumgr,
Client *client,
ISimpleTextureSource *tsrc,
+ ISoundManager *sound_manager,
IFormSource* fs_src,
TextDest* txt_dst,
const std::string &formspecPrepend,
@@ -161,13 +163,14 @@ public:
static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
- const std::string &formspecPrepend);
+ const std::string &formspecPrepend, ISoundManager *sound_manager);
void setFormSpec(const std::string &formspec_string,
const InventoryLocation &current_inventory_location)
{
m_formspec_string = formspec_string;
m_current_inventory_location = current_inventory_location;
+ m_is_form_regenerated = false;
regenerateGui(m_screensize_old);
}
@@ -250,7 +253,7 @@ public:
void updateSelectedItem();
ItemStack verifySelectedItem();
- void acceptInput(FormspecQuitMode quitmode);
+ void acceptInput(FormspecQuitMode quitmode=quit_mode_no);
bool preprocessEvent(const SEvent& event);
bool OnEvent(const SEvent& event);
bool doPause;
@@ -293,16 +296,22 @@ protected:
InventoryManager *m_invmgr;
ISimpleTextureSource *m_tsrc;
+ ISoundManager *m_sound_manager;
Client *m_client;
std::string m_formspec_string;
std::string m_formspec_prepend;
InventoryLocation m_current_inventory_location;
+ // Default true because we can't control regeneration on resizing, but
+ // we can control cases when the formspec is shown intentionally.
+ bool m_is_form_regenerated = true;
+
std::vector<GUIInventoryList *> m_inventorylists;
std::vector<ListRingSpec> m_inventory_rings;
std::vector<gui::IGUIElement *> m_backgrounds;
std::unordered_map<std::string, bool> field_close_on_enter;
+ std::unordered_map<std::string, bool> m_dropdown_index_event;
std::vector<FieldSpec> m_fields;
std::vector<std::pair<FieldSpec, GUITable *>> m_tables;
std::vector<std::pair<FieldSpec, gui::IGUICheckBox *>> m_checkboxes;
@@ -338,16 +347,16 @@ protected:
video::SColor m_default_tooltip_bgcolor;
video::SColor m_default_tooltip_color;
-
private:
IFormSource *m_form_src;
TextDest *m_text_dst;
+ std::string m_last_formname;
u16 m_formspec_version = 1;
std::string m_focused_element = "";
JoystickController *m_joystick;
bool m_show_debug = false;
- typedef struct {
+ struct parserData {
bool explicit_size;
bool real_coordinates;
u8 simple_field_count;
@@ -358,7 +367,6 @@ private:
core::rect<s32> rect;
v2s32 basepos;
v2u32 screensize;
- std::string focused_fieldname;
GUITable::TableOptions table_options;
GUITable::TableColumns table_columns;
gui::IGUIElement *current_parent = nullptr;
@@ -376,16 +384,16 @@ private:
// used to restore table selection/scroll/treeview state
std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
- } parserData;
+ };
- typedef struct {
+ struct fs_key_pending {
bool key_up;
bool key_down;
bool key_enter;
bool key_escape;
- } fs_key_pendig;
+ };
- fs_key_pendig current_keys_pending;
+ fs_key_pending current_keys_pending;
std::string current_field_enter_pending = "";
std::vector<std::string> m_hovered_item_tooltips;
@@ -438,6 +446,8 @@ private:
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 parseSetFocus(const std::string &element);
+ void parseModel(parserData *data, const std::string &element);
void tryClose();
@@ -451,30 +461,8 @@ private:
*/
void legacySortElements(core::list<IGUIElement *>::Iterator from);
- /**
- * check if event is part of a double click
- * @param event event to evaluate
- * @return true/false if a doubleclick was detected
- */
- bool DoubleClickDetection(const SEvent event);
-
- struct clickpos
- {
- v2s32 pos;
- s64 time;
- };
- clickpos m_doubleclickdetect[2];
-
int m_btn_height;
gui::IGUIFont *m_font = nullptr;
-
- /* If true, remap a double-click (or double-tap) action to ESC. This is so
- * that, for example, Android users can double-tap to close a formspec.
- *
- * This value can (currently) only be set by the class constructor
- * and the default value for the setting is true.
- */
- bool m_remap_dbl_click;
};
class FormspecFormSource: public IFormSource
diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp
index 88931cdf9..ccfdcb81d 100644
--- a/src/gui/guiHyperText.cpp
+++ b/src/gui/guiHyperText.cpp
@@ -1088,7 +1088,7 @@ bool GUIHyperText::OnEvent(const SEvent &event)
if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
checkHover(event.MouseInput.X, event.MouseInput.Y);
- if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+ if (event.MouseInput.Event == EMIE_MOUSE_WHEEL && m_vscrollbar->isVisible()) {
m_vscrollbar->setPos(m_vscrollbar->getPos() -
event.MouseInput.Wheel * m_vscrollbar->getSmallStep());
m_text_scrollpos.Y = -m_vscrollbar->getPos();
diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp
index dfdb60448..183d72165 100644
--- a/src/gui/guiInventoryList.cpp
+++ b/src/gui/guiInventoryList.cpp
@@ -104,8 +104,6 @@ void GUIInventoryList::draw()
&& m_invmgr->getInventory(selected_item->inventoryloc) == inv
&& selected_item->listname == m_listname
&& selected_item->i == item_i;
- core::rect<s32> clipped_rect(rect);
- clipped_rect.clipAgainst(AbsoluteClippingRect);
bool hovering = m_hovered_i == item_i;
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp
index da0e25c23..4dcb47779 100644
--- a/src/gui/guiKeyChangeMenu.cpp
+++ b/src/gui/guiKeyChangeMenu.cpp
@@ -248,7 +248,7 @@ bool GUIKeyChangeMenu::acceptInput()
{
for (key_setting *k : key_settings) {
std::string default_key;
- g_settings->getDefaultNoEx(k->setting_name, default_key);
+ Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
if (k->key.sym() != default_key)
g_settings->set(k->setting_name, k->key.sym());
@@ -360,7 +360,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
{
if (!canTakeFocus(event.GUIEvent.Element))
{
- dstream << "GUIMainMenu: Not allowing focus change."
+ infostream << "GUIKeyChangeMenu: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
diff --git a/src/gui/guiMainMenu.h b/src/gui/guiMainMenu.h
index 43a3b1a33..1dca8bf2d 100644
--- a/src/gui/guiMainMenu.h
+++ b/src/gui/guiMainMenu.h
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
#include <string>
#include <list>
diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp
index 965a2d6f7..74cd62f5b 100644
--- a/src/gui/guiPasswordChange.cpp
+++ b/src/gui/guiPasswordChange.cpp
@@ -79,7 +79,11 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
/*
Calculate new sizes and positions
*/
+#ifdef __ANDROID__
+ const float s = m_gui_scale * porting::getDisplayDensity() / 2;
+#else
const float s = m_gui_scale;
+#endif
DesiredRect = core::rect<s32>(
screensize.X / 2 - 580 * s / 2,
screensize.Y / 2 - 300 * s / 2,
@@ -236,7 +240,7 @@ bool GUIPasswordChange::OnEvent(const SEvent &event)
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST &&
isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
- dstream << "GUIPasswordChange: Not allowing focus change."
+ infostream << "GUIPasswordChange: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
@@ -289,6 +293,10 @@ bool GUIPasswordChange::getAndroidUIInput()
if (!hasAndroidUIInput())
return false;
+ // still waiting
+ if (porting::getInputDialogState() == -1)
+ return true;
+
gui::IGUIElement *e = nullptr;
if (m_jni_field_name == "old_password")
e = getElementFromId(ID_oldPassword);
@@ -296,12 +304,13 @@ bool GUIPasswordChange::getAndroidUIInput()
e = getElementFromId(ID_newPassword1);
else if (m_jni_field_name == "new_password_2")
e = getElementFromId(ID_newPassword2);
-
- if (e) {
- std::string text = porting::getInputDialogValue();
- e->setText(utf8_to_wide(text).c_str());
- }
m_jni_field_name.clear();
+
+ if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
+ return false;
+
+ std::string text = porting::getInputDialogValue();
+ e->setText(utf8_to_wide(text).c_str());
return false;
}
#endif
diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp
new file mode 100644
index 000000000..5f4c50b91
--- /dev/null
+++ b/src/gui/guiScene.cpp
@@ -0,0 +1,266 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+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 "guiScene.h"
+
+#include <SViewFrustum.h>
+#include <IAnimatedMeshSceneNode.h>
+#include <ILightSceneNode.h>
+#include "porting.h"
+
+GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id)
+ : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect)
+{
+ m_driver = env->getVideoDriver();
+ m_smgr = smgr->createNewSceneManager(false);
+
+ m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f));
+ m_cam->setFOV(30.f * core::DEGTORAD);
+
+ scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam);
+ light->setRadius(1000.f);
+
+ m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
+}
+
+GUIScene::~GUIScene()
+{
+ setMesh(nullptr);
+
+ m_smgr->drop();
+}
+
+scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh)
+{
+ if (m_mesh) {
+ m_mesh->remove();
+ m_mesh = nullptr;
+ }
+
+ if (!mesh)
+ return nullptr;
+
+ m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh);
+ m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter());
+ m_mesh->animateJoints();
+ return m_mesh;
+}
+
+void GUIScene::setTexture(u32 idx, video::ITexture *texture)
+{
+ video::SMaterial &material = m_mesh->getMaterial(idx);
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialTypeParam = 0.5f;
+ material.TextureLayer[0].Texture = texture;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+}
+
+void GUIScene::draw()
+{
+ // Control rotation speed based on time
+ u64 new_time = porting::getTimeMs();
+ u64 dtime_ms = 0;
+ if (m_last_time != 0)
+ dtime_ms = porting::getDeltaMs(m_last_time, new_time);
+ m_last_time = new_time;
+
+ core::rect<s32> oldViewPort = m_driver->getViewPort();
+ m_driver->setViewPort(getAbsoluteClippingRect());
+ core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect();
+
+ if (m_bgcolor != 0) {
+ Environment->getSkin()->draw3DSunkenPane(
+ this, m_bgcolor, false, true, borderRect, 0);
+ }
+
+ core::dimension2d<s32> size = getAbsoluteClippingRect().getSize();
+ m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height);
+
+ if (!m_target) {
+ updateCamera(m_smgr->addEmptySceneNode());
+ rotateCamera(v3f(0.f));
+ m_cam->bindTargetAndRotation(true);
+ }
+
+ cameraLoop();
+
+ // Continuous rotation
+ if (m_inf_rot)
+ rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f));
+
+ m_smgr->drawAll();
+
+ if (m_initial_rotation && m_mesh) {
+ rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f));
+ calcOptimalDistance();
+
+ m_initial_rotation = false;
+ }
+
+ m_driver->setViewPort(oldViewPort);
+}
+
+bool GUIScene::OnEvent(const SEvent &event)
+{
+ if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) {
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+ return true;
+ } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) {
+ if (event.MouseInput.isLeftPressed()) {
+ m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y);
+
+ rotateCamera(v3f(
+ m_last_pos.Y - m_curr_pos.Y,
+ m_curr_pos.X - m_last_pos.X, 0.f));
+
+ m_last_pos = m_curr_pos;
+ return true;
+ }
+ }
+ }
+
+ return gui::IGUIElement::OnEvent(event);
+}
+
+void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles)
+{
+ StyleSpec::State state = StyleSpec::STATE_DEFAULT;
+ StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state);
+
+ setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor));
+}
+
+/**
+ * Sets the frame loop range for the mesh
+ */
+void GUIScene::setFrameLoop(s32 begin, s32 end)
+{
+ if (m_mesh->getStartFrame() != begin || m_mesh->getEndFrame() != end)
+ m_mesh->setFrameLoop(begin, end);
+}
+
+/* Camera control functions */
+
+inline void GUIScene::calcOptimalDistance()
+{
+ core::aabbox3df box = m_mesh->getBoundingBox();
+ f32 width = box.MaxEdge.X - box.MinEdge.X;
+ f32 height = box.MaxEdge.Y - box.MinEdge.Y;
+ f32 depth = box.MaxEdge.Z - box.MinEdge.Z;
+ f32 max_width = width > depth ? width : depth;
+
+ const scene::SViewFrustum *f = m_cam->getViewFrustum();
+ f32 cam_far = m_cam->getFarValue();
+ f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength();
+ f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength();
+
+ core::recti rect = getAbsolutePosition();
+ f32 zoomX = rect.getWidth() / max_width;
+ f32 zoomY = rect.getHeight() / height;
+ f32 dist;
+
+ if (zoomX < zoomY)
+ dist = (max_width / (far_width / cam_far)) + (0.5f * max_width);
+ else
+ dist = (height / (far_height / cam_far)) + (0.5f * max_width);
+
+ m_cam_distance = dist;
+ m_update_cam = true;
+}
+
+void GUIScene::updateCamera(scene::ISceneNode *target)
+{
+ m_target = target;
+ updateTargetPos();
+
+ m_last_target_pos = m_target_pos;
+ updateCameraPos();
+
+ m_update_cam = true;
+}
+
+void GUIScene::updateTargetPos()
+{
+ m_last_target_pos = m_target_pos;
+ m_target->updateAbsolutePosition();
+ m_target_pos = m_target->getAbsolutePosition();
+}
+
+void GUIScene::setCameraRotation(v3f rot)
+{
+ correctBounds(rot);
+
+ core::matrix4 mat;
+ mat.setRotationDegrees(rot);
+
+ m_cam_pos = v3f(0.f, 0.f, m_cam_distance);
+ mat.rotateVect(m_cam_pos);
+
+ m_cam_pos += m_target_pos;
+ m_cam->setPosition(m_cam_pos);
+ m_update_cam = false;
+}
+
+bool GUIScene::correctBounds(v3f &rot)
+{
+ const float ROTATION_MAX_1 = 60.0f;
+ const float ROTATION_MAX_2 = 300.0f;
+
+ // Limit and correct the rotation when needed
+ if (rot.X < 90.f) {
+ if (rot.X > ROTATION_MAX_1) {
+ rot.X = ROTATION_MAX_1;
+ return true;
+ }
+ } else if (rot.X < ROTATION_MAX_2) {
+ rot.X = ROTATION_MAX_2;
+ return true;
+ }
+
+ // Not modified
+ return false;
+}
+
+void GUIScene::cameraLoop()
+{
+ updateCameraPos();
+ updateTargetPos();
+
+ if (m_target_pos != m_last_target_pos)
+ m_update_cam = true;
+
+ if (m_update_cam) {
+ m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance;
+
+ v3f rot = getCameraRotation();
+ if (correctBounds(rot))
+ setCameraRotation(rot);
+
+ m_cam->setPosition(m_cam_pos);
+ m_cam->setTarget(m_target_pos);
+
+ m_update_cam = false;
+ }
+}
diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h
new file mode 100644
index 000000000..08eb7f350
--- /dev/null
+++ b/src/gui/guiScene.h
@@ -0,0 +1,86 @@
+/*
+Minetest
+Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com>
+
+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.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "ICameraSceneNode.h"
+#include "StyleSpec.h"
+
+using namespace irr;
+
+class GUIScene : public gui::IGUIElement
+{
+public:
+ GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr,
+ gui::IGUIElement *parent, core::recti rect, s32 id = -1);
+
+ ~GUIScene();
+
+ scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr);
+ void setTexture(u32 idx, video::ITexture *texture);
+ void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; };
+ void setFrameLoop(s32 begin, s32 end);
+ void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; };
+ void setRotation(v2f rot) noexcept { m_custom_rot = rot; };
+ void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; };
+ void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles);
+
+ virtual void draw();
+ virtual bool OnEvent(const SEvent &event);
+
+private:
+ void calcOptimalDistance();
+ void updateTargetPos();
+ void updateCamera(scene::ISceneNode *target);
+ void setCameraRotation(v3f rot);
+ /// @return true indicates that the rotation was corrected
+ bool correctBounds(v3f &rot);
+ void cameraLoop();
+
+ void updateCameraPos() { m_cam_pos = m_cam->getPosition(); };
+ v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); };
+ void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); };
+
+ scene::ISceneManager *m_smgr;
+ video::IVideoDriver *m_driver;
+ scene::ICameraSceneNode *m_cam;
+ scene::ISceneNode *m_target = nullptr;
+ scene::IAnimatedMeshSceneNode *m_mesh = nullptr;
+
+ f32 m_cam_distance = 50.f;
+
+ u64 m_last_time = 0;
+
+ v3f m_cam_pos;
+ v3f m_target_pos;
+ v3f m_last_target_pos;
+ // Cursor positions
+ v2f m_curr_pos;
+ v2f m_last_pos;
+ // Initial rotation
+ v2f m_custom_rot;
+
+ bool m_mouse_ctrl = true;
+ bool m_update_cam = false;
+ bool m_inf_rot = false;
+ bool m_initial_rotation = true;
+
+ video::SColor m_bgcolor = 0;
+};
diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp
index b04ccb9d5..c6a03f3e4 100644
--- a/src/gui/guiScrollBar.cpp
+++ b/src/gui/guiScrollBar.cpp
@@ -21,7 +21,7 @@ GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s3
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)
+ large_step(50), drag_offset(0), page_size(100), border_size(0)
{
refreshControls();
setNotClipped(false);
diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h
index 29493bb99..d18f8e875 100644
--- a/src/gui/guiScrollBar.h
+++ b/src/gui/guiScrollBar.h
@@ -68,7 +68,6 @@ private:
s32 max_pos;
s32 small_step;
s32 large_step;
- u32 last_change;
s32 drag_offset;
s32 page_size;
s32 border_size;
diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp
index 88cdc7057..0fe4c41bd 100644
--- a/src/gui/guiScrollContainer.cpp
+++ b/src/gui/guiScrollContainer.cpp
@@ -56,6 +56,18 @@ bool GUIScrollContainer::OnEvent(const SEvent &event)
return IGUIElement::OnEvent(event);
}
+void GUIScrollContainer::draw()
+{
+ if (isVisible()) {
+ core::list<IGUIElement *>::Iterator it = Children.begin();
+ for (; it != Children.end(); ++it)
+ if ((*it)->isNotClipped() ||
+ AbsoluteClippingRect.isRectCollided(
+ (*it)->getAbsolutePosition()))
+ (*it)->draw();
+ }
+}
+
void GUIScrollContainer::updateScrolling()
{
s32 pos = m_scrollbar->getPos();
diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h
index a0306291e..9e3ec6e93 100644
--- a/src/gui/guiScrollContainer.h
+++ b/src/gui/guiScrollContainer.h
@@ -32,6 +32,8 @@ public:
virtual bool OnEvent(const SEvent &event) override;
+ virtual void draw() override;
+
inline void onScrollEvent(gui::IGUIElement *caller)
{
if (caller == m_scrollbar)
diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp
index 8892a00b4..e09209bd9 100644
--- a/src/gui/guiSkin.cpp
+++ b/src/gui/guiSkin.cpp
@@ -29,7 +29,7 @@ GUISkin::GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver)
{
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_FACE] = video::SColor(220,100,100,100);
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);
diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp
index c705e17fb..cab2e19fd 100644
--- a/src/gui/guiTable.cpp
+++ b/src/gui/guiTable.cpp
@@ -56,7 +56,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
m_font = skin->getFont();
if (m_font) {
m_font->grab();
- m_rowheight = m_font->getDimension(L"A").Height + 4;
+ m_rowheight = m_font->getDimension(L"Ay").Height + 4;
m_rowheight = MYMAX(m_rowheight, 1);
}
@@ -586,6 +586,31 @@ void GUITable::setSelected(s32 index)
}
}
+void GUITable::setOverrideFont(IGUIFont *font)
+{
+ if (m_font == font)
+ return;
+
+ if (font == nullptr)
+ font = Environment->getSkin()->getFont();
+
+ if (m_font)
+ m_font->drop();
+
+ m_font = font;
+ m_font->grab();
+
+ m_rowheight = m_font->getDimension(L"Ay").Height + 4;
+ m_rowheight = MYMAX(m_rowheight, 1);
+
+ updateScrollBar();
+}
+
+IGUIFont *GUITable::getOverrideFont() const
+{
+ return m_font;
+}
+
GUITable::DynamicData GUITable::getDynamicData() const
{
DynamicData dyndata;
diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h
index 11093ea72..76a0e94d0 100644
--- a/src/gui/guiTable.h
+++ b/src/gui/guiTable.h
@@ -123,6 +123,12 @@ public:
// Autoscroll to make the selected row fully visible
void setSelected(s32 index);
+ //! 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 = nullptr);
+
+ //! Gets the override font (if any)
+ virtual gui::IGUIFont *getOverrideFont() const;
+
/* Get selection, scroll position and opened (sub)trees */
DynamicData getDynamicData() const;
diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp
index 07b11248c..f17cfa986 100644
--- a/src/gui/guiVolumeChange.cpp
+++ b/src/gui/guiVolumeChange.cpp
@@ -171,7 +171,7 @@ bool GUIVolumeChange::OnEvent(const SEvent& event)
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
- dstream << "GUIMainMenu: Not allowing focus change."
+ infostream << "GUIVolumeChange: Not allowing focus change."
<< std::endl;
// Returning true disables focus change
return true;
diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
index 10395423c..0f09ea746 100644
--- a/src/gui/intlGUIEditBox.cpp
+++ b/src/gui/intlGUIEditBox.cpp
@@ -59,9 +59,7 @@ namespace gui
intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
IGUIEnvironment* environment, IGUIElement* parent, s32 id,
const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
- : IGUIEditBox(environment, parent, id, rectangle),
- Border(border), FrameRect(rectangle),
- m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable)
+ : GUIEditBox(environment, parent, id, rectangle, border, writable)
{
#ifdef _DEBUG
setDebugName("intlintlGUIEditBox");
@@ -70,10 +68,10 @@ intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
Text = text;
if (Environment)
- Operator = Environment->getOSOperator();
+ m_operator = Environment->getOSOperator();
- if (Operator)
- Operator->grab();
+ if (m_operator)
+ m_operator->grab();
// this element can be tabbed to
setTabStop(true);
@@ -82,12 +80,12 @@ intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
IGUISkin *skin = 0;
if (Environment)
skin = Environment->getSkin();
- if (Border && skin)
+ if (m_border && skin)
{
- FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
- FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
- FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
- FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
}
if (skin && has_vscrollbar) {
@@ -104,96 +102,11 @@ intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
setWritable(writable);
}
-
-//! destructor
-intlGUIEditBox::~intlGUIEditBox()
-{
- if (OverrideFont)
- OverrideFont->drop();
-
- if (Operator)
- Operator->drop();
-
- if (m_vscrollbar)
- m_vscrollbar->drop();
-}
-
-
-//! Sets another skin independent font.
-void intlGUIEditBox::setOverrideFont(IGUIFont* font)
-{
- if (OverrideFont == font)
- return;
-
- if (OverrideFont)
- OverrideFont->drop();
-
- OverrideFont = font;
-
- if (OverrideFont)
- OverrideFont->grab();
-
- breakText();
-}
-
-IGUIFont * intlGUIEditBox::getOverrideFont() const
-{
- return OverrideFont;
-}
-
-//! Get the font which is used right now for drawing
-IGUIFont* intlGUIEditBox::getActiveFont() const
-{
- if ( OverrideFont )
- return OverrideFont;
- IGUISkin* skin = Environment->getSkin();
- if (skin)
- return skin->getFont();
- return 0;
-}
-
-//! Sets another color for the text.
-void intlGUIEditBox::setOverrideColor(video::SColor color)
-{
- OverrideColor = color;
- OverrideColorEnabled = true;
-}
-
-video::SColor intlGUIEditBox::getOverrideColor() const
-{
- return OverrideColor;
-}
-
-//! Turns the border on or off
-void intlGUIEditBox::setDrawBorder(bool border)
-{
- Border = border;
-}
-
//! Sets whether to draw the background
void intlGUIEditBox::setDrawBackground(bool draw)
{
}
-//! Sets if the text should use the overide color or the color in the gui skin.
-void intlGUIEditBox::enableOverrideColor(bool enable)
-{
- OverrideColorEnabled = enable;
-}
-
-bool intlGUIEditBox::isOverrideColorEnabled() const
-{
- return OverrideColorEnabled;
-}
-
-//! Enables or disables word wrap
-void intlGUIEditBox::setWordWrap(bool enable)
-{
- WordWrap = enable;
- breakText();
-}
-
-
void intlGUIEditBox::updateAbsolutePosition()
{
core::rect<s32> oldAbsoluteRect(AbsoluteRect);
@@ -205,557 +118,6 @@ void intlGUIEditBox::updateAbsolutePosition()
}
-//! Checks if word wrap is enabled
-bool intlGUIEditBox::isWordWrapEnabled() const
-{
- return WordWrap;
-}
-
-
-//! Enables or disables newlines.
-void intlGUIEditBox::setMultiLine(bool enable)
-{
- MultiLine = enable;
-}
-
-
-//! Checks if multi line editing is enabled
-bool intlGUIEditBox::isMultiLineEnabled() const
-{
- return MultiLine;
-}
-
-
-void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
-{
- PasswordBox = passwordBox;
- if (PasswordBox)
- {
- PasswordChar = passwordChar;
- setMultiLine(false);
- setWordWrap(false);
- BrokenText.clear();
- }
-}
-
-
-bool intlGUIEditBox::isPasswordBox() const
-{
- return PasswordBox;
-}
-
-
-//! Sets text justification
-void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
-{
- HAlign = horizontal;
- VAlign = vertical;
-}
-
-
-//! called if an event happened.
-bool intlGUIEditBox::OnEvent(const SEvent& event)
-{
- if (IsEnabled)
- {
-
- switch(event.EventType)
- {
- case EET_GUI_EVENT:
- if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
- {
- if (event.GUIEvent.Caller == this)
- {
- MouseMarking = false;
- setTextMarkers(0,0);
- }
- }
- break;
- case EET_KEY_INPUT_EVENT:
- {
-#if (defined(__linux__) || defined(__FreeBSD__)) || defined(__DragonFly__)
- // ################################################################
- // ValkaTR:
- // This part is the difference from the original intlGUIEditBox
- // It converts UTF-8 character into a UCS-2 (wchar_t)
- wchar_t wc = L'_';
- mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
-
- //printf( "char: %lc (%u) \r\n", wc, wc );
-
- SEvent irrevent(event);
- irrevent.KeyInput.Char = wc;
- // ################################################################
-
- if (processKey(irrevent))
- return true;
-#else
- if (processKey(event))
- return true;
-#endif // defined(linux)
-
- break;
- }
- case EET_MOUSE_INPUT_EVENT:
- if (processMouse(event))
- return true;
- break;
- default:
- break;
- }
- }
-
- return IGUIElement::OnEvent(event);
-}
-
-
-bool intlGUIEditBox::processKey(const SEvent& event)
-{
- if (!event.KeyInput.PressedDown)
- return false;
-
- bool textChanged = false;
- s32 newMarkBegin = MarkBegin;
- s32 newMarkEnd = MarkEnd;
-
- // control shortcut handling
-
- if (event.KeyInput.Control)
- {
- // german backlash '\' entered with control + '?'
- if ( event.KeyInput.Char == '\\' )
- {
- inputChar(event.KeyInput.Char);
- return true;
- }
-
- switch(event.KeyInput.Key)
- {
- case KEY_KEY_A:
- // select all
- newMarkBegin = 0;
- newMarkEnd = Text.size();
- break;
- case KEY_KEY_C:
- // copy to clipboard
- if (!PasswordBox && Operator && MarkBegin != MarkEnd)
- {
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- core::stringc s;
- s = Text.subString(realmbgn, realmend - realmbgn).c_str();
- Operator->copyToClipboard(s.c_str());
- }
- break;
- case KEY_KEY_X:
- // cut to the clipboard
- if (!PasswordBox && Operator && MarkBegin != MarkEnd) {
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- // copy
- core::stringc sc;
- sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
- Operator->copyToClipboard(sc.c_str());
-
- if (IsEnabled && m_writable) {
- // delete
- core::stringw s;
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
-
- CursorPos = realmbgn;
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
- }
- }
- break;
- case KEY_KEY_V:
- if (!IsEnabled || !m_writable)
- break;
-
- // paste from the clipboard
- if (Operator)
- {
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- // add new character
- const c8* p = Operator->getTextFromClipboard();
- if (p)
- {
- if (MarkBegin == MarkEnd)
- {
- // insert text
- core::stringw s = Text.subString(0, CursorPos);
- s.append(p);
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
-
- if (!Max || s.size()<=Max) // thx to Fish FH for fix
- {
- Text = s;
- s = p;
- CursorPos += s.size();
- }
- }
- else
- {
- // replace text
-
- core::stringw s = Text.subString(0, realmbgn);
- s.append(p);
- s.append( Text.subString(realmend, Text.size()-realmend) );
-
- if (!Max || s.size()<=Max) // thx to Fish FH for fix
- {
- Text = s;
- s = p;
- CursorPos = realmbgn + s.size();
- }
- }
- }
-
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
- }
- break;
- case KEY_HOME:
- // move/highlight to start of text
- if (event.KeyInput.Shift)
- {
- newMarkEnd = CursorPos;
- newMarkBegin = 0;
- CursorPos = 0;
- }
- else
- {
- CursorPos = 0;
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
- break;
- case KEY_END:
- // move/highlight to end of text
- if (event.KeyInput.Shift)
- {
- newMarkBegin = CursorPos;
- newMarkEnd = Text.size();
- CursorPos = 0;
- }
- else
- {
- CursorPos = Text.size();
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
- break;
- default:
- return false;
- }
- }
- // default keyboard handling
- else
- switch(event.KeyInput.Key)
- {
- case KEY_END:
- {
- s32 p = Text.size();
- if (WordWrap || MultiLine)
- {
- p = getLineFromPos(CursorPos);
- p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
- if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
- p-=1;
- }
-
- if (event.KeyInput.Shift)
- {
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
-
- newMarkEnd = p;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
- CursorPos = p;
- BlinkStartTime = porting::getTimeMs();
- }
- break;
- case KEY_HOME:
- {
-
- s32 p = 0;
- if (WordWrap || MultiLine)
- {
- p = getLineFromPos(CursorPos);
- p = BrokenTextPositions[p];
- }
-
- if (event.KeyInput.Shift)
- {
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
- newMarkEnd = p;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
- CursorPos = p;
- BlinkStartTime = porting::getTimeMs();
- }
- break;
- case KEY_RETURN:
- if (MultiLine)
- {
- inputChar(L'\n');
- return true;
- }
- else
- {
- sendGuiEvent( EGET_EDITBOX_ENTER );
- }
- break;
- case KEY_LEFT:
-
- if (event.KeyInput.Shift)
- {
- if (CursorPos > 0)
- {
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
-
- newMarkEnd = CursorPos-1;
- }
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- if (CursorPos > 0) CursorPos--;
- BlinkStartTime = porting::getTimeMs();
- break;
-
- case KEY_RIGHT:
- if (event.KeyInput.Shift)
- {
- if (Text.size() > (u32)CursorPos)
- {
- if (MarkBegin == MarkEnd)
- newMarkBegin = CursorPos;
-
- newMarkEnd = CursorPos+1;
- }
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- if (Text.size() > (u32)CursorPos) CursorPos++;
- BlinkStartTime = porting::getTimeMs();
- break;
- case KEY_UP:
- if (MultiLine || (WordWrap && BrokenText.size() > 1) )
- {
- s32 lineNo = getLineFromPos(CursorPos);
- s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
- if (lineNo > 0)
- {
- s32 cp = CursorPos - BrokenTextPositions[lineNo];
- if ((s32)BrokenText[lineNo-1].size() < cp)
- CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
- else
- CursorPos = BrokenTextPositions[lineNo-1] + cp;
- }
-
- if (event.KeyInput.Shift)
- {
- newMarkBegin = mb;
- newMarkEnd = CursorPos;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- }
- else
- {
- return false;
- }
- break;
- case KEY_DOWN:
- if (MultiLine || (WordWrap && BrokenText.size() > 1) )
- {
- s32 lineNo = getLineFromPos(CursorPos);
- s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
- if (lineNo < (s32)BrokenText.size()-1)
- {
- s32 cp = CursorPos - BrokenTextPositions[lineNo];
- if ((s32)BrokenText[lineNo+1].size() < cp)
- CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
- else
- CursorPos = BrokenTextPositions[lineNo+1] + cp;
- }
-
- if (event.KeyInput.Shift)
- {
- newMarkBegin = mb;
- newMarkEnd = CursorPos;
- }
- else
- {
- newMarkBegin = 0;
- newMarkEnd = 0;
- }
-
- }
- else
- {
- return false;
- }
- break;
-
- case KEY_BACK:
- if (!this->IsEnabled || !m_writable)
- break;
-
- if (!Text.empty()) {
- core::stringw s;
-
- if (MarkBegin != MarkEnd)
- {
- // delete marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
-
- CursorPos = realmbgn;
- }
- else
- {
- // delete text behind cursor
- if (CursorPos>0)
- s = Text.subString(0, CursorPos-1);
- else
- s = L"";
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
- Text = s;
- --CursorPos;
- }
-
- if (CursorPos < 0)
- CursorPos = 0;
- BlinkStartTime = porting::getTimeMs();
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
- }
- break;
- case KEY_DELETE:
- if (!this->IsEnabled || !m_writable)
- break;
-
- if (!Text.empty()) {
- core::stringw s;
-
- if (MarkBegin != MarkEnd)
- {
- // delete marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- s = Text.subString(0, realmbgn);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
-
- CursorPos = realmbgn;
- }
- else
- {
- // delete text before cursor
- s = Text.subString(0, CursorPos);
- s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
- Text = s;
- }
-
- if (CursorPos > (s32)Text.size())
- CursorPos = (s32)Text.size();
-
- BlinkStartTime = porting::getTimeMs();
- newMarkBegin = 0;
- newMarkEnd = 0;
- textChanged = true;
- }
- break;
-
- case KEY_ESCAPE:
- case KEY_TAB:
- case KEY_SHIFT:
- case KEY_F1:
- case KEY_F2:
- case KEY_F3:
- case KEY_F4:
- case KEY_F5:
- case KEY_F6:
- case KEY_F7:
- case KEY_F8:
- case KEY_F9:
- case KEY_F10:
- case KEY_F11:
- case KEY_F12:
- case KEY_F13:
- case KEY_F14:
- case KEY_F15:
- case KEY_F16:
- case KEY_F17:
- case KEY_F18:
- case KEY_F19:
- case KEY_F20:
- case KEY_F21:
- case KEY_F22:
- case KEY_F23:
- case KEY_F24:
- // ignore these keys
- return false;
-
- default:
- inputChar(event.KeyInput.Char);
- return true;
- }
-
- // Set new text markers
- setTextMarkers( newMarkBegin, newMarkEnd );
-
- // break the text if it has changed
- if (textChanged)
- {
- breakText();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
- }
-
- calculateScrollPos();
-
- return true;
-}
-
-
//! draws the element and its children
void intlGUIEditBox::draw()
{
@@ -768,31 +130,31 @@ void intlGUIEditBox::draw()
if (!skin)
return;
- FrameRect = AbsoluteRect;
+ m_frame_rect = AbsoluteRect;
// draw the border
- if (Border)
+ if (m_border)
{
if (m_writable) {
skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
- false, true, FrameRect, &AbsoluteClippingRect);
+ false, true, m_frame_rect, &AbsoluteClippingRect);
}
- FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
- FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
- FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
- FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
}
updateVScrollBar();
- core::rect<s32> localClipRect = FrameRect;
+ core::rect<s32> localClipRect = m_frame_rect;
localClipRect.clipAgainst(AbsoluteClippingRect);
// draw the text
- IGUIFont* font = OverrideFont;
- if (!OverrideFont)
+ IGUIFont* font = m_override_font;
+ if (!m_override_font)
font = skin->getFont();
s32 cursorLine = 0;
@@ -800,7 +162,7 @@ void intlGUIEditBox::draw()
if (font)
{
- if (LastBreakFont != font)
+ if (m_last_break_font != font)
{
breakText();
}
@@ -813,23 +175,23 @@ void intlGUIEditBox::draw()
core::stringw s, s2;
// get mark position
- const bool ml = (!PasswordBox && (WordWrap || MultiLine));
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline));
+ const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
- const s32 lineCount = ml ? BrokenText.size() : 1;
+ const s32 lineCount = ml ? m_broken_text.size() : 1;
// Save the override color information.
// Then, alter it if the edit box is disabled.
- const bool prevOver = OverrideColorEnabled;
- const video::SColor prevColor = OverrideColor;
+ const bool prevOver = m_override_color_enabled;
+ const video::SColor prevColor = m_override_color;
if (!Text.empty()) {
- if (!IsEnabled && !OverrideColorEnabled)
+ if (!IsEnabled && !m_override_color_enabled)
{
- OverrideColorEnabled = true;
- OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+ m_override_color_enabled = true;
+ m_override_color = skin->getColor(EGDC_GRAY_TEXT);
}
for (s32 i=0; i < lineCount; ++i)
@@ -838,43 +200,43 @@ void intlGUIEditBox::draw()
// clipping test - don't draw anything outside the visible area
core::rect<s32> c = localClipRect;
- c.clipAgainst(CurrentTextRect);
+ c.clipAgainst(m_current_text_rect);
if (!c.isValid())
continue;
// get current line
- if (PasswordBox)
+ if (m_passwordbox)
{
- if (BrokenText.size() != 1)
+ if (m_broken_text.size() != 1)
{
- BrokenText.clear();
- BrokenText.push_back(core::stringw());
+ m_broken_text.clear();
+ m_broken_text.emplace_back();
}
- if (BrokenText[0].size() != Text.size())
+ if (m_broken_text[0].size() != Text.size())
{
- BrokenText[0] = Text;
+ m_broken_text[0] = Text;
for (u32 q = 0; q < Text.size(); ++q)
{
- BrokenText[0] [q] = PasswordChar;
+ m_broken_text[0] [q] = m_passwordchar;
}
}
- txtLine = &BrokenText[0];
+ txtLine = &m_broken_text[0];
startPos = 0;
}
else
{
- txtLine = ml ? &BrokenText[i] : &Text;
- startPos = ml ? BrokenTextPositions[i] : 0;
+ txtLine = ml ? &m_broken_text[i] : &Text;
+ startPos = ml ? m_broken_text_positions[i] : 0;
}
// draw normal text
- font->draw(txtLine->c_str(), CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ font->draw(txtLine->c_str(), m_current_text_rect,
+ m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
false, true, &localClipRect);
// draw mark and marked text
- if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
+ if (focus && m_mark_begin != m_mark_end && i >= hlineStart && i < hlineStart + hlineCount)
{
s32 mbegin = 0, mend = 0;
@@ -903,47 +265,47 @@ void intlGUIEditBox::draw()
else
mend = font->getDimension(txtLine->c_str()).Width;
- CurrentTextRect.UpperLeftCorner.X += mbegin;
- CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
+ m_current_text_rect.UpperLeftCorner.X += mbegin;
+ m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin;
// draw mark
- skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
+ skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &localClipRect);
// draw marked text
s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
if (!s.empty())
- font->draw(s.c_str(), CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+ font->draw(s.c_str(), m_current_text_rect,
+ m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
false, true, &localClipRect);
}
}
// Return the override color information to its previous settings.
- OverrideColorEnabled = prevOver;
- OverrideColor = prevColor;
+ m_override_color_enabled = prevOver;
+ m_override_color = prevColor;
}
// draw cursor
- if (WordWrap || MultiLine)
+ if (m_word_wrap || m_multiline)
{
- cursorLine = getLineFromPos(CursorPos);
- txtLine = &BrokenText[cursorLine];
- startPos = BrokenTextPositions[cursorLine];
+ cursorLine = getLineFromPos(m_cursor_pos);
+ txtLine = &m_broken_text[cursorLine];
+ startPos = m_broken_text_positions[cursorLine];
}
- s = txtLine->subString(0,CursorPos-startPos);
+ s = txtLine->subString(0,m_cursor_pos-startPos);
charcursorpos = font->getDimension(s.c_str()).Width +
- font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
+ font->getKerningWidth(L"_", m_cursor_pos-startPos > 0 ? &((*txtLine)[m_cursor_pos-startPos-1]) : 0);
if (m_writable) {
- if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350) {
+ if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
setTextRect(cursorLine);
- CurrentTextRect.UpperLeftCorner.X += charcursorpos;
+ m_current_text_rect.UpperLeftCorner.X += charcursorpos;
- font->draw(L"_", CurrentTextRect,
- OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ font->draw(L"_", m_current_text_rect,
+ m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
false, true, &localClipRect);
}
}
@@ -954,154 +316,11 @@ void intlGUIEditBox::draw()
}
-//! Sets the new caption of this element.
-void intlGUIEditBox::setText(const wchar_t* text)
-{
- Text = text;
- if (u32(CursorPos) > Text.size())
- CursorPos = Text.size();
- HScrollPos = 0;
- breakText();
-}
-
-
-//! Enables or disables automatic scrolling with cursor position
-//! \param enable: If set to true, the text will move around with the cursor position
-void intlGUIEditBox::setAutoScroll(bool enable)
-{
- AutoScroll = enable;
-}
-
-
-//! Checks to see if automatic scrolling is enabled
-//! \return true if automatic scrolling is enabled, false if not
-bool intlGUIEditBox::isAutoScrollEnabled() const
-{
- return AutoScroll;
-}
-
-
-//! Gets the area of the text in the edit box
-//! \return Returns the size in pixels of the text
-core::dimension2du intlGUIEditBox::getTextDimension()
-{
- core::rect<s32> ret;
-
- setTextRect(0);
- ret = CurrentTextRect;
-
- for (u32 i=1; i < BrokenText.size(); ++i)
- {
- setTextRect(i);
- ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
- ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
- }
-
- return core::dimension2du(ret.getSize());
-}
-
-
-//! Sets the maximum amount of characters which may be entered in the box.
-//! \param max: Maximum amount of characters. If 0, the character amount is
-//! infinity.
-void intlGUIEditBox::setMax(u32 max)
-{
- Max = max;
-
- if (Text.size() > Max && Max != 0)
- Text = Text.subString(0, Max);
-}
-
-
-//! Returns maximum amount of characters, previously set by setMax();
-u32 intlGUIEditBox::getMax() const
-{
- return Max;
-}
-
-
-bool intlGUIEditBox::processMouse(const SEvent& event)
-{
- switch(event.MouseInput.Event)
- {
- case irr::EMIE_LMOUSE_LEFT_UP:
- if (Environment->hasFocus(this))
- {
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- if (MouseMarking)
- {
- setTextMarkers( MarkBegin, CursorPos );
- }
- MouseMarking = false;
- calculateScrollPos();
- return true;
- }
- break;
- case irr::EMIE_MOUSE_MOVED:
- {
- if (MouseMarking)
- {
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- setTextMarkers( MarkBegin, CursorPos );
- calculateScrollPos();
- return true;
- }
- }
- break;
- case EMIE_LMOUSE_PRESSED_DOWN:
- if (!Environment->hasFocus(this))
- {
- BlinkStartTime = porting::getTimeMs();
- MouseMarking = true;
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
- setTextMarkers(CursorPos, CursorPos );
- calculateScrollPos();
- return true;
- }
- else
- {
- if (!AbsoluteClippingRect.isPointInside(
- core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
- return false;
- }
-
-
- // move cursor
- CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-
- s32 newMarkBegin = MarkBegin;
- if (!MouseMarking)
- newMarkBegin = CursorPos;
-
- MouseMarking = true;
- setTextMarkers( newMarkBegin, CursorPos);
- calculateScrollPos();
- return true;
- }
- break;
- case EMIE_MOUSE_WHEEL:
- if (m_vscrollbar && m_vscrollbar->isVisible()) {
- s32 pos = m_vscrollbar->getPos();
- s32 step = m_vscrollbar->getSmallStep();
- m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
- }
- break;
- default:
- break;
- }
-
- return false;
-}
-
-
s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
{
- IGUIFont* font = OverrideFont;
- IGUISkin* skin = Environment->getSkin();
- if (!OverrideFont)
- font = skin->getFont();
+ IGUIFont* font = getActiveFont();
- const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+ const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
core::stringw *txtLine = NULL;
s32 startPos = 0;
@@ -1110,29 +329,29 @@ s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
for (; curr_line_idx < lineCount; ++curr_line_idx) {
setTextRect(curr_line_idx);
- if (curr_line_idx == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
- y = CurrentTextRect.UpperLeftCorner.Y;
- if (curr_line_idx == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y)
- y = CurrentTextRect.LowerRightCorner.Y;
+ if (curr_line_idx == 0 && y < m_current_text_rect.UpperLeftCorner.Y)
+ y = m_current_text_rect.UpperLeftCorner.Y;
+ if (curr_line_idx == lineCount - 1 && y > m_current_text_rect.LowerRightCorner.Y)
+ y = m_current_text_rect.LowerRightCorner.Y;
// is it inside this region?
- if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) {
+ if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) {
// we've found the clicked line
- txtLine = (WordWrap || MultiLine) ? &BrokenText[curr_line_idx] : &Text;
- startPos = (WordWrap || MultiLine) ? BrokenTextPositions[curr_line_idx] : 0;
+ txtLine = (m_word_wrap || m_multiline) ? &m_broken_text[curr_line_idx] : &Text;
+ startPos = (m_word_wrap || m_multiline) ? m_broken_text_positions[curr_line_idx] : 0;
break;
}
}
- if (x < CurrentTextRect.UpperLeftCorner.X)
- x = CurrentTextRect.UpperLeftCorner.X;
- else if (x > CurrentTextRect.LowerRightCorner.X)
- x = CurrentTextRect.LowerRightCorner.X;
+ if (x < m_current_text_rect.UpperLeftCorner.X)
+ x = m_current_text_rect.UpperLeftCorner.X;
+ else if (x > m_current_text_rect.LowerRightCorner.X)
+ x = m_current_text_rect.LowerRightCorner.X;
- s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+ s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - m_current_text_rect.UpperLeftCorner.X);
// Special handling for last line, if we are on limits, add 1 extra shift because idx
// will be the last char, not null char of the wstring
- if (curr_line_idx == lineCount - 1 && x == CurrentTextRect.LowerRightCorner.X)
+ if (curr_line_idx == lineCount - 1 && x == m_current_text_rect.LowerRightCorner.X)
idx++;
return rangelim(idx + startPos, 0, S32_MAX);
@@ -1144,20 +363,20 @@ void intlGUIEditBox::breakText()
{
IGUISkin* skin = Environment->getSkin();
- if ((!WordWrap && !MultiLine) || !skin)
+ if ((!m_word_wrap && !m_multiline) || !skin)
return;
- BrokenText.clear(); // need to reallocate :/
- BrokenTextPositions.set_used(0);
+ m_broken_text.clear(); // need to reallocate :/
+ m_broken_text_positions.clear();
- IGUIFont* font = OverrideFont;
- if (!OverrideFont)
+ IGUIFont* font = m_override_font;
+ if (!m_override_font)
font = skin->getFont();
if (!font)
return;
- LastBreakFont = font;
+ m_last_break_font = font;
core::stringw line;
core::stringw word;
@@ -1165,7 +384,7 @@ void intlGUIEditBox::breakText()
s32 lastLineStart = 0;
s32 size = Text.size();
s32 length = 0;
- s32 elWidth = RelativeRect.getWidth() - 6;
+ s32 elWidth = RelativeRect.getWidth() - m_scrollbar_width - 10;
wchar_t c;
for (s32 i=0; i<size; ++i)
@@ -1190,7 +409,7 @@ void intlGUIEditBox::breakText()
}
// don't break if we're not a multi-line edit box
- if (!MultiLine)
+ if (!m_multiline)
lineBreak = false;
if (c == L' ' || c == 0 || i == (size-1))
@@ -1201,12 +420,12 @@ void intlGUIEditBox::breakText()
s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
s32 worldlgth = font->getDimension(word.c_str()).Width;
- if (WordWrap && length + worldlgth + whitelgth > elWidth)
+ if (m_word_wrap && length + worldlgth + whitelgth > elWidth)
{
// break to next line
length = worldlgth;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
+ m_broken_text.push_back(line);
+ m_broken_text_positions.push_back(lastLineStart);
lastLineStart = i - (s32)word.size();
line = word;
}
@@ -1229,8 +448,8 @@ void intlGUIEditBox::breakText()
{
line += whitespace;
line += word;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
+ m_broken_text.push_back(line);
+ m_broken_text_positions.push_back(lastLineStart);
lastLineStart = i+1;
line = L"";
word = L"";
@@ -1247,8 +466,8 @@ void intlGUIEditBox::breakText()
line += whitespace;
line += word;
- BrokenText.push_back(line);
- BrokenTextPositions.push_back(lastLineStart);
+ m_broken_text.push_back(line);
+ m_broken_text_positions.push_back(lastLineStart);
}
@@ -1260,16 +479,16 @@ void intlGUIEditBox::setTextRect(s32 line)
if (!skin)
return;
- IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ IGUIFont* font = m_override_font ? m_override_font : skin->getFont();
if (!font)
return;
// get text dimension
- const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
- if (WordWrap || MultiLine)
+ const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
+ if (m_word_wrap || m_multiline)
{
- d = font->getDimension(BrokenText[line].c_str());
+ d = font->getDimension(m_broken_text[line].c_str());
}
else
{
@@ -1279,197 +498,112 @@ void intlGUIEditBox::setTextRect(s32 line)
d.Height += font->getKerningHeight();
// justification
- switch (HAlign)
+ switch (m_halign)
{
case EGUIA_CENTER:
// align to h centre
- CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
- CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
+ m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth()/2) - (d.Width/2);
+ m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth()/2) + (d.Width/2);
break;
case EGUIA_LOWERRIGHT:
// align to right edge
- CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
- CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
+ m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width;
+ m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth();
break;
default:
// align to left edge
- CurrentTextRect.UpperLeftCorner.X = 0;
- CurrentTextRect.LowerRightCorner.X = d.Width;
+ m_current_text_rect.UpperLeftCorner.X = 0;
+ m_current_text_rect.LowerRightCorner.X = d.Width;
}
- switch (VAlign)
+ switch (m_valign)
{
case EGUIA_CENTER:
// align to v centre
- CurrentTextRect.UpperLeftCorner.Y =
- (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+ m_current_text_rect.UpperLeftCorner.Y =
+ (m_frame_rect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
break;
case EGUIA_LOWERRIGHT:
// align to bottom edge
- CurrentTextRect.UpperLeftCorner.Y =
- FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+ m_current_text_rect.UpperLeftCorner.Y =
+ m_frame_rect.getHeight() - lineCount*d.Height + d.Height*line;
break;
default:
// align to top edge
- CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
+ m_current_text_rect.UpperLeftCorner.Y = d.Height*line;
break;
}
- CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
- CurrentTextRect.LowerRightCorner.X -= HScrollPos;
- CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
- CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
-
- CurrentTextRect += FrameRect.UpperLeftCorner;
-
-}
-
-
-s32 intlGUIEditBox::getLineFromPos(s32 pos)
-{
- if (!WordWrap && !MultiLine)
- return 0;
-
- s32 i=0;
- while (i < (s32)BrokenTextPositions.size())
- {
- if (BrokenTextPositions[i] > pos)
- return i-1;
- ++i;
- }
- return (s32)BrokenTextPositions.size() - 1;
-}
-
-
-void intlGUIEditBox::inputChar(wchar_t c)
-{
- if (!IsEnabled || !m_writable)
- return;
-
- if (c != 0)
- {
- if (Text.size() < Max || Max == 0)
- {
- core::stringw s;
+ m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos;
+ m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos;
+ m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos;
+ m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height;
- if (MarkBegin != MarkEnd)
- {
- // replace marked text
- const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
- const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
- s = Text.subString(0, realmbgn);
- s.append(c);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
- CursorPos = realmbgn+1;
- }
- else
- {
- // add new character
- s = Text.subString(0, CursorPos);
- s.append(c);
- s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
- Text = s;
- ++CursorPos;
- }
+ m_current_text_rect += m_frame_rect.UpperLeftCorner;
- BlinkStartTime = porting::getTimeMs();
- setTextMarkers(0, 0);
- }
- }
- breakText();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
- calculateScrollPos();
}
-
void intlGUIEditBox::calculateScrollPos()
{
- if (!AutoScroll)
+ if (!m_autoscroll)
return;
// calculate horizontal scroll position
- s32 cursLine = getLineFromPos(CursorPos);
+ s32 cursLine = getLineFromPos(m_cursor_pos);
setTextRect(cursLine);
// don't do horizontal scrolling when wordwrap is enabled.
- if (!WordWrap)
+ if (!m_word_wrap)
{
// get cursor position
IGUISkin* skin = Environment->getSkin();
if (!skin)
return;
- IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ IGUIFont* font = m_override_font ? m_override_font : skin->getFont();
if (!font)
return;
- core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
- s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
+ core::stringw *txtLine = m_multiline ? &m_broken_text[cursLine] : &Text;
+ s32 cPos = m_multiline ? m_cursor_pos - m_broken_text_positions[cursLine] : m_cursor_pos;
- s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
+ s32 cStart = m_current_text_rect.UpperLeftCorner.X + m_hscroll_pos +
font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
s32 cEnd = cStart + font->getDimension(L"_ ").Width;
- if (FrameRect.LowerRightCorner.X < cEnd)
- HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
- else if (FrameRect.UpperLeftCorner.X > cStart)
- HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
+ if (m_frame_rect.LowerRightCorner.X < cEnd)
+ m_hscroll_pos = cEnd - m_frame_rect.LowerRightCorner.X;
+ else if (m_frame_rect.UpperLeftCorner.X > cStart)
+ m_hscroll_pos = cStart - m_frame_rect.UpperLeftCorner.X;
else
- HScrollPos = 0;
+ m_hscroll_pos = 0;
// todo: adjust scrollbar
}
- if (!WordWrap && !MultiLine)
+ if (!m_word_wrap && !m_multiline)
return;
// vertical scroll position
- if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y)
- VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards
- else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y)
- VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards
+ if (m_frame_rect.LowerRightCorner.Y < m_current_text_rect.LowerRightCorner.Y)
+ m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y; // scrolling downwards
+ else if (m_frame_rect.UpperLeftCorner.Y > m_current_text_rect.UpperLeftCorner.Y)
+ m_vscroll_pos += m_current_text_rect.UpperLeftCorner.Y - m_frame_rect.UpperLeftCorner.Y; // scrolling upwards
// todo: adjust scrollbar
if (m_vscrollbar)
- m_vscrollbar->setPos(VScrollPos);
+ m_vscrollbar->setPos(m_vscroll_pos);
}
-//! set text markers
-void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
-{
- if ( begin != MarkBegin || end != MarkEnd )
- {
- MarkBegin = begin;
- MarkEnd = end;
- sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
- }
-}
-
-//! send some gui event to parent
-void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
-{
- if ( Parent )
- {
- SEvent e;
- e.EventType = EET_GUI_EVENT;
- e.GUIEvent.Caller = this;
- e.GUIEvent.Element = 0;
- e.GUIEvent.EventType = type;
-
- Parent->OnEvent(e);
- }
-}
//! Create a vertical scrollbar
void intlGUIEditBox::createVScrollBar()
{
s32 fontHeight = 1;
- if (OverrideFont) {
- fontHeight = OverrideFont->getDimension(L"").Height;
+ if (m_override_font) {
+ fontHeight = m_override_font->getDimension(L"").Height;
} else {
if (IGUISkin* skin = Environment->getSkin()) {
if (IGUIFont* font = skin->getFont()) {
@@ -1478,10 +612,8 @@ void intlGUIEditBox::createVScrollBar()
}
}
- RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
-
- irr::core::rect<s32> scrollbarrect = FrameRect;
- scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
+ irr::core::rect<s32> scrollbarrect = m_frame_rect;
+ scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,
scrollbarrect, false, true);
@@ -1490,110 +622,5 @@ void intlGUIEditBox::createVScrollBar()
m_vscrollbar->setLargeStep(10 * fontHeight);
}
-//! Update the vertical scrollbar (visibilty & scroll position)
-void intlGUIEditBox::updateVScrollBar()
-{
- if (!m_vscrollbar)
- return;
-
- // OnScrollBarChanged(...)
- if (m_vscrollbar->getPos() != VScrollPos) {
- s32 deltaScrollY = m_vscrollbar->getPos() - VScrollPos;
- CurrentTextRect.UpperLeftCorner.Y -= deltaScrollY;
- CurrentTextRect.LowerRightCorner.Y -= deltaScrollY;
-
- s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
- 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
- VScrollPos = m_vscrollbar->getPos();
- }
- }
-
- // check if a vertical scrollbar is needed ?
- if (getTextDimension().Height > (u32) FrameRect.getHeight()) {
- 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) {
- AbsoluteRect.LowerRightCorner.X -= m_scrollbar_width;
-
- m_vscrollbar->setVisible(true);
- }
- } else {
- if (m_vscrollbar->isVisible()) {
- AbsoluteRect.LowerRightCorner.X += m_scrollbar_width;
-
- VScrollPos = 0;
- m_vscrollbar->setPos(0);
- m_vscrollbar->setMax(1);
- m_vscrollbar->setPageSize(s32(getTextDimension().Height));
- m_vscrollbar->setVisible(false);
- }
- }
-}
-
-void intlGUIEditBox::setWritable(bool can_write_text)
-{
- m_writable = can_write_text;
-}
-
-//! Writes attributes of the element.
-void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
-{
- // IGUIEditBox::serializeAttributes(out,options);
-
- out->addBool ("OverrideColorEnabled",OverrideColorEnabled );
- out->addColor ("OverrideColor", OverrideColor);
- // out->addFont("OverrideFont",OverrideFont);
- out->addInt ("MaxChars", Max);
- out->addBool ("WordWrap", WordWrap);
- out->addBool ("MultiLine", MultiLine);
- out->addBool ("AutoScroll", AutoScroll);
- out->addBool ("PasswordBox", PasswordBox);
- core::stringw ch = L" ";
- ch[0] = PasswordChar;
- out->addString("PasswordChar", ch.c_str());
- out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
- out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
- out->addBool ("Writable", m_writable);
-
- IGUIEditBox::serializeAttributes(out,options);
-}
-
-
-//! Reads attributes of the element
-void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
-{
- IGUIEditBox::deserializeAttributes(in,options);
-
- setOverrideColor(in->getAttributeAsColor("OverrideColor"));
- enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
- setMax(in->getAttributeAsInt("MaxChars"));
- setWordWrap(in->getAttributeAsBool("WordWrap"));
- setMultiLine(in->getAttributeAsBool("MultiLine"));
- setAutoScroll(in->getAttributeAsBool("AutoScroll"));
- core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
- if (ch.empty())
- setPasswordBox(in->getAttributeAsBool("PasswordBox"));
- else
- setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
- setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
- (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
- setWritable(in->getAttributeAsBool("Writable"));
- // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
-}
-
-
} // end namespace gui
} // end namespace irr
diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h
index 9d643495e..007fe1c93 100644
--- a/src/gui/intlGUIEditBox.h
+++ b/src/gui/intlGUIEditBox.h
@@ -7,16 +7,15 @@
#include "IrrCompileConfig.h"
//#ifdef _IRR_COMPILE_WITH_GUI_
-#include <IGUIEditBox.h>
+#include "guiEditBox.h"
#include "irrArray.h"
#include "IOSOperator.h"
-#include "guiScrollBar.h"
namespace irr
{
namespace gui
{
- class intlGUIEditBox : public IGUIEditBox
+ class intlGUIEditBox : public GUIEditBox
{
public:
@@ -26,113 +25,19 @@ namespace gui
bool writable = true, bool has_vscrollbar = false);
//! destructor
- virtual ~intlGUIEditBox();
-
- //! Sets another skin independent font.
- virtual void setOverrideFont(IGUIFont* font=0);
-
- //! Gets the override font (if any)
- /** \return The override font (may be 0) */
- virtual IGUIFont* getOverrideFont() const;
-
- //! Get the font which is used right now for drawing
- /** Currently this is the override font when one is set and the
- font of the active skin otherwise */
- virtual IGUIFont* getActiveFont() const;
-
- //! Sets another color for the text.
- virtual void setOverrideColor(video::SColor color);
-
- //! Gets the override color
- virtual video::SColor getOverrideColor() const;
-
- //! Sets if the text should use the overide color or the
- //! color in the gui skin.
- virtual void enableOverrideColor(bool enable);
-
- //! Checks if an override color is enabled
- /** \return true if the override color is enabled, false otherwise */
- virtual bool isOverrideColorEnabled(void) const;
+ virtual ~intlGUIEditBox() {}
//! Sets whether to draw the background
virtual void setDrawBackground(bool draw);
virtual bool isDrawBackgroundEnabled() const { return true; }
- //! Turns the border on or off
- virtual void setDrawBorder(bool border);
-
- virtual bool isDrawBorderEnabled() const { return Border; }
-
- //! Enables or disables word wrap for using the edit box as multiline text editor.
- virtual void setWordWrap(bool enable);
-
- //! Checks if word wrap is enabled
- //! \return true if word wrap is enabled, false otherwise
- virtual bool isWordWrapEnabled() const;
-
- //! Enables or disables newlines.
- /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
- instead a newline character will be inserted. */
- virtual void setMultiLine(bool enable);
-
- //! Checks if multi line editing is enabled
- //! \return true if mult-line is enabled, false otherwise
- virtual bool isMultiLineEnabled() const;
-
- //! Enables or disables automatic scrolling with cursor position
- //! \param enable: If set to true, the text will move around with the cursor position
- virtual void setAutoScroll(bool enable);
-
- //! Checks to see if automatic scrolling is enabled
- //! \return true if automatic scrolling is enabled, false if not
- virtual bool isAutoScrollEnabled() const;
-
- //! Gets the size area of the text in the edit box
- //! \return Returns the size in pixels of the text
- virtual core::dimension2du getTextDimension();
-
- //! Sets text justification
- virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
-
- //! called if an event happened.
- virtual bool OnEvent(const SEvent& event);
-
//! draws the element and its children
virtual void draw();
- //! Sets the new caption of this element.
- virtual void setText(const wchar_t* text);
-
- //! Sets the maximum amount of characters which may be entered in the box.
- //! \param max: Maximum amount of characters. If 0, the character amount is
- //! infinity.
- virtual void setMax(u32 max);
-
- //! Returns maximum amount of characters, previously set by setMax();
- virtual u32 getMax() const;
-
- //! Sets whether the edit box is a password box. Setting this to true will
- /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
- \param passwordBox: true to enable password, false to disable
- \param passwordChar: the character that is displayed instead of letters */
- virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
-
- //! Returns true if the edit box is currently a password box.
- virtual bool isPasswordBox() const;
-
//! Updates the absolute position, splits text if required
virtual void updateAbsolutePosition();
- //! set true if this EditBox is writable
- virtual void setWritable(bool can_write_text);
-
- //! Writes attributes of the element.
- virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
-
- //! Reads attributes of the element
- virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
-
virtual void setCursorChar(const wchar_t cursorChar) {}
virtual wchar_t getCursorChar() const { return L'|'; }
@@ -143,64 +48,17 @@ namespace gui
protected:
//! Breaks the single text line.
- void breakText();
+ virtual void breakText();
//! sets the area of the given line
- void setTextRect(s32 line);
- //! returns the line number that the cursor is on
- s32 getLineFromPos(s32 pos);
- //! adds a letter to the edit box
- void inputChar(wchar_t c);
+ virtual void setTextRect(s32 line);
+
//! calculates the current scroll position
void calculateScrollPos();
- //! send some gui event to parent
- void sendGuiEvent(EGUI_EVENT_TYPE type);
- //! set text markers
- void setTextMarkers(s32 begin, s32 end);
- bool processKey(const SEvent& event);
- bool processMouse(const SEvent& event);
s32 getCursorPos(s32 x, s32 y);
//! Create a vertical scrollbar
void createVScrollBar();
-
- //! Update the vertical scrollbar (visibilty & scroll position)
- void updateVScrollBar();
-
- bool MouseMarking = false;
- bool Border;
- bool OverrideColorEnabled = false;
- s32 MarkBegin = 0;
- s32 MarkEnd = 0;
-
- video::SColor OverrideColor = video::SColor(101,255,255,255);
- gui::IGUIFont *OverrideFont = nullptr;
- gui::IGUIFont *LastBreakFont = nullptr;
- IOSOperator *Operator = nullptr;
-
- u64 BlinkStartTime = 0;
- s32 CursorPos = 0;
- s32 HScrollPos = 0;
- s32 VScrollPos = 0; // scroll position in characters
- u32 Max = 0;
-
- bool WordWrap = false;
- bool MultiLine = false;
- bool AutoScroll = true;
- bool PasswordBox = false;
- wchar_t PasswordChar = L'*';
- EGUI_ALIGNMENT HAlign = EGUIA_UPPERLEFT;
- EGUI_ALIGNMENT VAlign = EGUIA_CENTER;
-
- core::array<core::stringw> BrokenText;
- core::array<s32> BrokenTextPositions;
-
- core::rect<s32> CurrentTextRect = core::rect<s32>(0,0,1,1);
- core::rect<s32> FrameRect; // temporary values
- u32 m_scrollbar_width;
- GUIScrollBar *m_vscrollbar;
- bool m_writable;
-
};
diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp
index a6fe7ebaf..0d3fb55f0 100644
--- a/src/gui/modalMenu.cpp
+++ b/src/gui/modalMenu.cpp
@@ -28,14 +28,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "touchscreengui.h"
#endif
-GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
- IMenuManager *menumgr) :
+// clang-format off
+GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
+ s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
core::rect<s32>(0, 0, 100, 100)),
#ifdef __ANDROID__
m_jni_field_name(""),
#endif
- m_menumgr(menumgr)
+ m_menumgr(menumgr),
+ m_remap_dbl_click(remap_dbl_click)
{
m_gui_scale = g_settings->getFloat("gui_scaling");
#ifdef __ANDROID__
@@ -45,6 +47,12 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
setVisible(true);
Environment->setFocus(this);
m_menumgr->createdMenu(this);
+
+ m_doubleclickdetect[0].time = 0;
+ m_doubleclickdetect[1].time = 0;
+
+ m_doubleclickdetect[0].pos = v2s32(0, 0);
+ m_doubleclickdetect[1].pos = v2s32(0, 0);
}
// clang-format on
@@ -112,6 +120,127 @@ void GUIModalMenu::removeChildren()
}
}
+// clang-format off
+bool GUIModalMenu::DoubleClickDetection(const SEvent &event)
+{
+ /* The following code is for capturing double-clicks of the mouse button
+ * and translating the double-click into an EET_KEY_INPUT_EVENT event
+ * -- which closes the form -- under some circumstances.
+ *
+ * There have been many github issues reporting this as a bug even though it
+ * was an intended feature. For this reason, remapping the double-click as
+ * an ESC must be explicitly set when creating this class via the
+ * /p remap_dbl_click parameter of the constructor.
+ */
+
+ if (!m_remap_dbl_click)
+ return false;
+
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
+ m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
+
+ m_doubleclickdetect[1].pos = m_pointer;
+ m_doubleclickdetect[1].time = porting::getTimeMs();
+ } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+ u64 delta = porting::getDeltaMs(
+ m_doubleclickdetect[0].time, porting::getTimeMs());
+ if (delta > 400)
+ return false;
+
+ double squaredistance = m_doubleclickdetect[0].pos.
+ getDistanceFromSQ(m_doubleclickdetect[1].pos);
+
+ if (squaredistance > (30 * 30)) {
+ return false;
+ }
+
+ SEvent translated{};
+ // translate doubleclick to escape
+ translated.EventType = EET_KEY_INPUT_EVENT;
+ translated.KeyInput.Key = KEY_ESCAPE;
+ translated.KeyInput.Control = false;
+ translated.KeyInput.Shift = false;
+ translated.KeyInput.PressedDown = true;
+ translated.KeyInput.Char = 0;
+ OnEvent(translated);
+
+ return true;
+ }
+
+ return false;
+}
+// clang-format on
+
+static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
+{
+ while (tocheck) {
+ if (tocheck == parent) {
+ return true;
+ }
+ tocheck = tocheck->getParent();
+ }
+ return false;
+}
+
+#ifdef __ANDROID__
+
+bool GUIModalMenu::simulateMouseEvent(
+ gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
+{
+ SEvent mouse_event{}; // value-initialized, not unitialized
+ mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
+ mouse_event.MouseInput.X = m_pointer.X;
+ mouse_event.MouseInput.Y = m_pointer.Y;
+ switch (touch_event) {
+ case ETIE_PRESSED_DOWN:
+ mouse_event.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
+ mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ break;
+ case ETIE_MOVED:
+ mouse_event.MouseInput.Event = EMIE_MOUSE_MOVED;
+ mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ break;
+ case ETIE_LEFT_UP:
+ mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
+ mouse_event.MouseInput.ButtonStates = 0;
+ break;
+ default:
+ return false;
+ }
+ if (preprocessEvent(mouse_event))
+ return true;
+ if (!target)
+ return false;
+ return target->OnEvent(mouse_event);
+}
+
+void GUIModalMenu::enter(gui::IGUIElement *hovered)
+{
+ sanity_check(!m_hovered);
+ m_hovered.grab(hovered);
+ SEvent gui_event{};
+ gui_event.EventType = EET_GUI_EVENT;
+ gui_event.GUIEvent.Caller = m_hovered.get();
+ gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
+ gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
+ m_hovered->OnEvent(gui_event);
+}
+
+void GUIModalMenu::leave()
+{
+ if (!m_hovered)
+ return;
+ SEvent gui_event{};
+ gui_event.EventType = EET_GUI_EVENT;
+ gui_event.GUIEvent.Caller = m_hovered.get();
+ gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
+ m_hovered->OnEvent(gui_event);
+ m_hovered.reset();
+}
+
+#endif
+
bool GUIModalMenu::preprocessEvent(const SEvent &event)
{
#ifdef __ANDROID__
@@ -159,92 +288,66 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
}
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
- SEvent translated;
- memset(&translated, 0, sizeof(SEvent));
- translated.EventType = EET_MOUSE_INPUT_EVENT;
- gui::IGUIElement *root = Environment->getRootGUIElement();
-
- if (!root) {
- errorstream << "GUIModalMenu::preprocessEvent"
- << " unable to get root element" << std::endl;
- return false;
- }
- gui::IGUIElement *hovered =
- root->getElementFromPoint(core::position2d<s32>(
- event.TouchInput.X, event.TouchInput.Y));
+ irr_ptr<GUIModalMenu> holder;
+ holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
- translated.MouseInput.X = event.TouchInput.X;
- translated.MouseInput.Y = event.TouchInput.Y;
- translated.MouseInput.Control = false;
-
- if (event.TouchInput.touchedCount == 1) {
- switch (event.TouchInput.Event) {
- case ETIE_PRESSED_DOWN:
+ switch ((int)event.TouchInput.touchedCount) {
+ case 1: {
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
m_down_pos = m_pointer;
- break;
- case ETIE_MOVED:
- m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_MOUSE_MOVED;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
- break;
- case ETIE_LEFT_UP:
- translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = 0;
- hovered = root->getElementFromPoint(m_down_pos);
- // we don't have a valid pointer element use last
- // known pointer pos
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
-
- // reset down pos
- m_down_pos = v2s32(0, 0);
- break;
- default:
- break;
+ gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
+ Environment->setFocus(hovered);
+ if (m_hovered != hovered) {
+ leave();
+ enter(hovered);
}
- } else if ((event.TouchInput.touchedCount == 2) &&
- (event.TouchInput.Event == ETIE_PRESSED_DOWN)) {
- hovered = root->getElementFromPoint(m_down_pos);
-
- translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
- if (hovered)
- hovered->OnEvent(translated);
-
- translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
-
- if (hovered)
- hovered->OnEvent(translated);
-
- return true;
- } else {
- // ignore unhandled 2 touch events (accidental moving for example)
+ gui::IGUIElement *focused = Environment->getFocus();
+ bool ret = simulateMouseEvent(focused, event.TouchInput.Event);
+ if (!ret && m_hovered != focused)
+ ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event);
+ if (event.TouchInput.Event == ETIE_LEFT_UP)
+ leave();
+ return ret;
+ }
+ case 2: {
+ if (event.TouchInput.Event != ETIE_PRESSED_DOWN)
+ return true; // ignore
+ auto focused = Environment->getFocus();
+ if (!focused)
+ return true;
+ SEvent rclick_event{};
+ rclick_event.EventType = EET_MOUSE_INPUT_EVENT;
+ rclick_event.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+ rclick_event.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
+ rclick_event.MouseInput.X = m_pointer.X;
+ rclick_event.MouseInput.Y = m_pointer.Y;
+ focused->OnEvent(rclick_event);
+ rclick_event.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
+ rclick_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ focused->OnEvent(rclick_event);
return true;
}
-
- // check if translated event needs to be preprocessed again
- if (preprocessEvent(translated))
+ default: // ignored
return true;
-
- if (hovered) {
- grab();
- bool retval = hovered->OnEvent(translated);
-
- if (event.TouchInput.Event == ETIE_LEFT_UP)
- // reset pointer
- m_pointer = v2s32(0, 0);
-
- drop();
- return retval;
}
}
#endif
+
+ if (event.EventType == EET_MOUSE_INPUT_EVENT) {
+ s32 x = event.MouseInput.X;
+ s32 y = event.MouseInput.Y;
+ gui::IGUIElement *hovered =
+ Environment->getRootGUIElement()->getElementFromPoint(
+ core::position2d<s32>(x, y));
+ if (!isChild(hovered, this)) {
+ if (DoubleClickDetection(event)) {
+ return true;
+ }
+ }
+ }
return false;
}
diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h
index 5bd70bb84..ed0da3205 100644
--- a/src/gui/modalMenu.h
+++ b/src/gui/modalMenu.h
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
+#include "irr_ptr.h"
#include "util/string.h"
class GUIModalMenu;
@@ -39,7 +40,7 @@ class GUIModalMenu : public gui::IGUIElement
{
public:
GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id,
- IMenuManager *menumgr);
+ IMenuManager *menumgr, bool remap_dbl_click = true);
virtual ~GUIModalMenu();
void allowFocusRemoval(bool allow);
@@ -50,8 +51,8 @@ public:
virtual void regenerateGui(v2u32 screensize) = 0;
virtual void drawMenu() = 0;
- virtual bool preprocessEvent(const SEvent& event);
- virtual bool OnEvent(const SEvent& event) { return false; };
+ virtual bool preprocessEvent(const SEvent &event);
+ virtual bool OnEvent(const SEvent &event) { return false; };
virtual bool pausesGame() { return false; } // Used for pause menu
#ifdef __ANDROID__
virtual bool getAndroidUIInput() { return false; }
@@ -62,6 +63,13 @@ protected:
virtual std::wstring getLabelByID(s32 id) = 0;
virtual std::string getNameByID(s32 id) = 0;
+ /**
+ * check if event is part of a double click
+ * @param event event to evaluate
+ * @return true/false if a doubleclick was detected
+ */
+ bool DoubleClickDetection(const SEvent &event);
+
v2s32 m_pointer;
v2s32 m_old_pointer; // Mouse position after previous mouse event
v2u32 m_screensize_old;
@@ -73,9 +81,32 @@ protected:
#ifdef HAVE_TOUCHSCREENGUI
bool m_touchscreen_visible = true;
#endif
+
private:
+ struct clickpos
+ {
+ v2s32 pos;
+ s64 time;
+ };
+ clickpos m_doubleclickdetect[2];
+
IMenuManager *m_menumgr;
+ /* If true, remap a double-click (or double-tap) action to ESC. This is so
+ * that, for example, Android users can double-tap to close a formspec.
+ *
+ * This value can (currently) only be set by the class constructor
+ * and the default value for the setting is true.
+ */
+ bool m_remap_dbl_click;
// This might be necessary to expose to the implementation if it
// wants to launch other menus
bool m_allow_focus_removal = false;
+
+#ifdef __ANDROID__
+ irr_ptr<gui::IGUIElement> m_hovered;
+
+ bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event);
+ void enter(gui::IGUIElement *element);
+ void leave();
+#endif
};
diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp
index 0d64aa618..e1a971462 100644
--- a/src/gui/touchscreengui.cpp
+++ b/src/gui/touchscreengui.cpp
@@ -881,8 +881,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
s32 dyj = event.TouchInput.Y - m_screensize.Y + button_size * 5.0f / 2.0f;
bool inside_joystick = (dxj * dxj + dyj * dyj <= button_size * button_size * 1.5 * 1.5);
- if (m_joystick_has_really_moved ||
- (!m_joystick_has_really_moved && inside_joystick) ||
+ if (m_joystick_has_really_moved || inside_joystick ||
(!m_fixed_joystick &&
distance_sq > m_touchscreen_threshold * m_touchscreen_threshold)) {
m_joystick_has_really_moved = true;
diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h
index 761d33207..0349624fa 100644
--- a/src/gui/touchscreengui.h
+++ b/src/gui/touchscreengui.h
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include "irrlichttypes.h"
#include <IEventReceiver.h>
#include <IGUIButton.h>
#include <IGUIEnvironment.h>