aboutsummaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/StyleSpec.h67
-rw-r--r--src/gui/guiBox.cpp87
-rw-r--r--src/gui/guiBox.h11
-rw-r--r--src/gui/guiButton.cpp21
-rw-r--r--src/gui/guiButton.h1
-rw-r--r--src/gui/guiChatConsole.cpp9
-rw-r--r--src/gui/guiChatConsole.h4
-rw-r--r--src/gui/guiConfirmRegistration.cpp19
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp4
-rw-r--r--src/gui/guiEngine.cpp18
-rw-r--r--src/gui/guiEngine.h12
-rw-r--r--src/gui/guiFormSpecMenu.cpp267
-rw-r--r--src/gui/guiFormSpecMenu.h26
-rw-r--r--src/gui/guiPasswordChange.cpp19
-rw-r--r--src/gui/guiScene.cpp257
-rw-r--r--src/gui/guiScene.h85
-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/intlGUIEditBox.cpp4
25 files changed, 829 insertions, 135 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index 7befba37c..91476ada6 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -16,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 67caf4f7b..f2844ce28 100644
--- a/src/gui/StyleSpec.h
+++ b/src/gui/StyleSpec.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include <algorithm>
#include <array>
+#include <vector>
#pragma once
@@ -50,6 +51,10 @@ public:
PADDING,
FONT,
FONT_SIZE,
+ COLORS,
+ BORDERCOLORS,
+ BORDERWIDTHS,
+ SOUND,
NUM_PROPERTIES,
NONE
};
@@ -106,6 +111,14 @@ public:
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 {
return NONE;
}
@@ -187,6 +200,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];
@@ -334,6 +383,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;
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 e0d6337cd..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);
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/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..204f9f9cc 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);
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
index 55c111df8..020a2796a 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,
@@ -257,12 +261,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/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
index 442406688..169425a9a 100644
--- a/src/gui/guiEditBoxWithScrollbar.cpp
+++ b/src/gui/guiEditBoxWithScrollbar.cpp
@@ -1028,7 +1028,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) {
@@ -1399,8 +1399,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,
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index b40707d01..c5ad5c323 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -170,6 +170,7 @@ GUIEngine::GUIEngine(JoystickController *joystick,
m_menumanager,
NULL /* &client */,
m_texture_source,
+ m_sound_manager,
m_formspecgui,
m_buttonhandler,
"",
@@ -297,10 +298,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 +372,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 +384,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);
}
}
diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h
index f9ad0fb0a..eef1ad8aa 100644
--- a/src/gui/guiEngine.h
+++ b/src/gui/guiEngine.h
@@ -29,22 +29,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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 */
@@ -277,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 601c5c18e..632b15992 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -48,6 +48,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()
@@ -65,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiScrollContainer.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
+#include "guiScene.h"
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
@@ -94,11 +96,13 @@ 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,
+ 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),
@@ -142,11 +146,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;
/*
@@ -613,6 +618,9 @@ 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 == m_focused_element) {
@@ -1019,6 +1027,9 @@ 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 == m_focused_element) {
@@ -1225,6 +1236,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);
@@ -1302,6 +1314,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);
@@ -1378,6 +1391,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);
@@ -1738,12 +1754,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();
@@ -1996,6 +2016,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
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);
@@ -2104,6 +2126,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
e->setTabHeight(geom.Y);
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) {
@@ -2192,6 +2217,9 @@ 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 == m_focused_element) {
@@ -2209,16 +2237,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;
@@ -2232,36 +2260,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)
@@ -2686,6 +2721,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &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() > 8 &&
+ m_formspec_version <= FORMSPEC_API_VERSION)) {
+ errorstream << "Invalid model element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return;
+ }
+
+ // Avoid length checks by resizing
+ if (parts.size() < 8)
+ parts.resize(8);
+
+ 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
+
+ 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(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);
+
+ 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
@@ -2882,6 +2997,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
+ if (type == "model") {
+ parseModel(data, description);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
@@ -3110,42 +3230,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
@@ -3474,10 +3594,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);
@@ -4387,6 +4511,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;
@@ -4430,6 +4556,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) {
@@ -4452,6 +4581,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);
@@ -4468,6 +4599,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 613acaa04..37106cb65 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -38,10 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class InventoryManager;
class ISimpleTextureSource;
class Client;
-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,7 +163,7 @@ 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)
@@ -294,6 +296,7 @@ protected:
InventoryManager *m_invmgr;
ISimpleTextureSource *m_tsrc;
+ ISoundManager *m_sound_manager;
Client *m_client;
std::string m_formspec_string;
@@ -353,7 +356,7 @@ private:
JoystickController *m_joystick;
bool m_show_debug = false;
- typedef struct {
+ struct parserData {
bool explicit_size;
bool real_coordinates;
u8 simple_field_count;
@@ -381,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;
@@ -444,6 +447,7 @@ private:
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();
diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp
index 5311c6fef..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,
@@ -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..08f119e07
--- /dev/null
+++ b/src/gui/guiScene.cpp
@@ -0,0 +1,257 @@
+/*
+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));
+}
+
+/* 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..707e6f66a
--- /dev/null
+++ b/src/gui/guiScene.h
@@ -0,0 +1,85 @@
+/*
+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 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/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
index 10395423c..8be63fd6f 100644
--- a/src/gui/intlGUIEditBox.cpp
+++ b/src/gui/intlGUIEditBox.cpp
@@ -1165,7 +1165,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)
@@ -1478,8 +1478,6 @@ void intlGUIEditBox::createVScrollBar()
}
}
- RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4;
-
irr::core::rect<s32> scrollbarrect = FrameRect;
scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1,