diff --git a/LICENSE.txt b/LICENSE.txt
index 503dd62d2..772f86728 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -102,6 +102,9 @@ grorp:
using the font "undefined medium" (https://undefined-medium.com/),
which is licensed under the SIL Open Font License, Version 1.1
modified by DS
+ textures/base/pack/dig_btn.png
+ textures/base/pack/place_btn.png
+ derived by editing the text in aux1_btn.svg
License of Luanti source code
-------------------------------
diff --git a/android/icons/dig_btn.svg b/android/icons/dig_btn.svg
new file mode 100644
index 000000000..cbfa69e6f
--- /dev/null
+++ b/android/icons/dig_btn.svg
@@ -0,0 +1,148 @@
+
+
diff --git a/android/icons/place_btn.svg b/android/icons/place_btn.svg
new file mode 100644
index 000000000..b32dfc018
--- /dev/null
+++ b/android/icons/place_btn.svg
@@ -0,0 +1,148 @@
+
+
diff --git a/builtin/common/settings/dlg_settings.lua b/builtin/common/settings/dlg_settings.lua
index 570c01cd5..6f0b772af 100644
--- a/builtin/common/settings/dlg_settings.lua
+++ b/builtin/common/settings/dlg_settings.lua
@@ -259,6 +259,17 @@ local function load()
["true"] = fgettext_ne("Enabled"),
["false"] = fgettext_ne("Disabled"),
}
+
+ get_setting_info("touch_interaction_style").option_labels = {
+ ["tap"] = fgettext_ne("Tap"),
+ ["tap_crosshair"] = fgettext_ne("Tap with crosshair"),
+ ["buttons_crosshair"] = fgettext("Buttons with crosshair"),
+ }
+
+ get_setting_info("touch_punch_gesture").option_labels = {
+ ["short_tap"] = fgettext_ne("Short tap"),
+ ["long_tap"] = fgettext_ne("Long tap"),
+ }
end
@@ -359,6 +370,7 @@ local function check_requirements(name, requires)
local video_driver = core.get_active_driver()
local touch_support = core.irrlicht_device_supports_touch()
local touch_controls = core.settings:get("touch_controls")
+ local touch_interaction_style = core.settings:get("touch_interaction_style")
local special = {
android = PLATFORM == "Android",
desktop = PLATFORM ~= "Android",
@@ -369,6 +381,7 @@ local function check_requirements(name, requires)
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
gles = video_driver:sub(1, 5) == "ogles",
+ touch_interaction_style_tap = touch_interaction_style ~= "buttons_crosshair",
}
for req_key, req_value in pairs(requires) do
diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt
index 5d3462e41..3ae55aecd 100644
--- a/builtin/settingtypes.txt
+++ b/builtin/settingtypes.txt
@@ -167,6 +167,36 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
# Requires: touch_support
touch_controls (Touchscreen controls) enum auto auto,true,false
+# The kind of digging/placing controls used.
+#
+# * Tap
+# Long/short tap anywhere on the screen to interact.
+# Interaction happens at finger position.
+#
+# * Tap with crosshair
+# Long/short tap anywhere on the screen to interact.
+# Interaction happens at crosshair position.
+#
+# * Buttons with crosshair
+# Use dedicated dig/place buttons to interact.
+# Interaction happens at crosshair position.
+#
+# Requires: touchscreen
+touch_interaction_style (Interaction style) enum tap tap,tap_crosshair,buttons_crosshair
+
+# The gesture for punching players/entities.
+# This can be overridden by games and mods.
+#
+# * Short tap
+# Easy to use and well-known from other games that shall not be named.
+#
+# * Long tap
+# Known from the classic Luanti mobile controls.
+# Combat is more or less impossible.
+#
+# Requires: touchscreen, touch_interaction_style_tap
+touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap
+
# Touchscreen sensitivity multiplier.
#
# Requires: touchscreen
@@ -182,12 +212,6 @@ touchscreen_threshold (Movement threshold) int 20 0 100
# Requires: touchscreen
touch_long_tap_delay (Threshold for long taps) int 400 100 1000
-# Use crosshair to select object instead of whole screen.
-# If enabled, a crosshair will be shown and will be used for selecting object.
-#
-# Requires: touchscreen
-touch_use_crosshair (Use crosshair for touch screen) bool false
-
# Fixes the position of virtual joystick.
# If disabled, virtual joystick will center to first-touch's position.
#
@@ -200,20 +224,6 @@ fixed_virtual_joystick (Fixed virtual joystick) bool false
# Requires: touchscreen
virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false
-# The gesture for punching players/entities.
-# This can be overridden by games and mods.
-#
-# * short_tap
-# Easy to use and well-known from other games that shall not be named.
-#
-# * long_tap
-# Known from the classic Luanti mobile controls.
-# Combat is more or less impossible.
-#
-# Requires: touchscreen
-touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap
-
-
[Graphics and Audio]
[*Graphics]
diff --git a/doc/texture_packs.md b/doc/texture_packs.md
index 256915b81..d386006f2 100644
--- a/doc/texture_packs.md
+++ b/doc/texture_packs.md
@@ -139,21 +139,34 @@ are placeholders intended to be overwritten by the game.
### Android textures
-* `drop_btn.png`
-* `fast_btn.png`
-* `fly_btn.png`
-* `jump_btn.png`
-* `noclip_btn.png`
+* `dig_btn.png`
+* `place_btn.png`
+
+* `jump_btn.png`
+* `down.png`
+* `zoom.png`
+* `aux1_btn.png`
+* `overflow_btn.png`
-* `camera_btn.png`
* `chat_btn.png`
* `inventory_btn.png`
-* `rangeview_btn.png`
-
-* `debug_btn.png`
-* `overflow_btn.png`
+* `drop_btn.png`
* `exit_btn.png`
+* `fly_btn.png`
+* `fast_btn.png`
+* `noclip_btn.png`
+* `debug_btn.png`
+* `camera_btn.png`
+* `rangeview_btn.png`
+* `minimap_btn.png`
+* `chat_hide_btn.png`
+* `chat_show_btn.png`
+
+* `joystick_off.png`
+* `joystick_bg.png`
+* `joystick_center.png`
+
Texture Overrides
-----------------
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 8b0eaa677..205fe5934 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -549,9 +549,9 @@ void set_default_settings()
settings->setDefault("touchscreen_sensitivity", "0.2");
settings->setDefault("touchscreen_threshold", "20");
settings->setDefault("touch_long_tap_delay", "400");
- settings->setDefault("touch_use_crosshair", "false");
settings->setDefault("fixed_virtual_joystick", "false");
settings->setDefault("virtual_joystick_triggers_aux1", "false");
+ settings->setDefault("touch_interaction_style", "tap");
settings->setDefault("touch_punch_gesture", "short_tap");
settings->setDefault("clickable_chat_weblinks", "true");
diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp
index d3c072d6f..4cea023ab 100644
--- a/src/gui/touchcontrols.cpp
+++ b/src/gui/touchcontrols.cpp
@@ -15,6 +15,7 @@
#include "client/guiscalingfilter.h"
#include "client/renderingengine.h"
#include "client/texturesource.h"
+#include "util/enum_string.h"
#include "util/numeric.h"
#include "irr_gui_ptr.h"
#include "IGUIImage.h"
@@ -78,15 +79,18 @@ bool TouchControls::buttonsHandlePress(std::vector &buttons, size_t
for (button_info &btn : buttons) {
if (btn.gui_button.get() == element) {
+ // Allow moving the camera with the same finger that holds dig/place.
+ bool absorb = btn.id != dig_id && btn.id != place_id;
+
assert(std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id) == btn.pointer_ids.end());
btn.pointer_ids.push_back(pointer_id);
if (btn.pointer_ids.size() > 1)
- return true;
+ return absorb;
buttonEmitAction(btn, true);
btn.repeat_counter = -BUTTON_REPEAT_DELAY;
- return true;
+ return absorb;
}
}
@@ -99,13 +103,16 @@ bool TouchControls::buttonsHandleRelease(std::vector &buttons, size
for (button_info &btn : buttons) {
auto it = std::find(btn.pointer_ids.begin(), btn.pointer_ids.end(), pointer_id);
if (it != btn.pointer_ids.end()) {
+ // Don't absorb since we didn't absorb the press event either.
+ bool absorb = btn.id != dig_id && btn.id != place_id;
+
btn.pointer_ids.erase(it);
if (!btn.pointer_ids.empty())
- return true;
+ return absorb;
buttonEmitAction(btn, false);
- return true;
+ return absorb;
}
}
@@ -117,6 +124,8 @@ bool TouchControls::buttonsStep(std::vector &buttons, float dtime)
bool has_pointers = false;
for (button_info &btn : buttons) {
+ if (btn.id == dig_id || btn.id == place_id)
+ continue; // key repeats would cause glitches here
if (btn.pointer_ids.empty())
continue;
has_pointers = true;
@@ -205,7 +214,7 @@ static const KeyPress &id_to_keypress(touch_gui_button_id id)
static const char *setting_names[] = {
- "touch_use_crosshair",
+ "touch_interaction_style",
"touchscreen_threshold", "touch_long_tap_delay",
"fixed_virtual_joystick", "virtual_joystick_triggers_aux1",
"touch_layout",
@@ -232,14 +241,20 @@ void TouchControls::settingChangedCallback(const std::string &name, void *data)
void TouchControls::readSettings()
{
- m_use_crosshair = g_settings->getBool("touch_use_crosshair");
+ const std::string &s = g_settings->get("touch_interaction_style");
+ if (!string_to_enum(es_TouchInteractionStyle, m_interaction_style, s)) {
+ m_interaction_style = TAP;
+ warningstream << "Invalid touch_interaction_style value" << std::endl;
+ }
+
m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold");
m_long_tap_delay = g_settings->getU16("touch_long_tap_delay");
m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick");
m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1");
- // Note that "fixed_virtual_joystick" and "virtual_joystick_triggers_aux1"
- // also affect the layout.
+ // Note that other settings also affect the layout:
+ // - ButtonLayout::loadFromSettings: "touch_interaction_style" and "virtual_joystick_triggers_aux1"
+ // - applyLayout: "fixed_virtual_joystick"
applyLayout(ButtonLayout::loadFromSettings());
}
@@ -305,8 +320,8 @@ void TouchControls::applyLayout(const ButtonLayout &layout)
overflow_buttons.erase(std::remove_if(
overflow_buttons.begin(), overflow_buttons.end(),
[&](touch_gui_button_id id) {
- // There's no sense in adding the overflow button to the overflow
- // menu (also, it's impossible since it doesn't have a keycode).
+ // There would be no sense in adding the overflow button to the
+ // overflow menu.
return !mayAddButton(id) || id == overflow_id;
}), overflow_buttons.end());
@@ -346,13 +361,10 @@ TouchControls::~TouchControls()
bool TouchControls::mayAddButton(touch_gui_button_id id)
{
- if (!ButtonLayout::isButtonAllowed(id))
- return false;
- if (id == aux1_id && m_joystick_triggers_aux1)
- return false;
- if (id != overflow_id && !id_to_keypress(id))
- return false;
- return true;
+ assert(ButtonLayout::isButtonValid(id));
+ assert(ButtonLayout::isButtonAllowed(id));
+ // The overflow button doesn't need a keycode to be valid.
+ return id == overflow_id || id_to_keypress(id);
}
void TouchControls::addButton(std::vector &buttons, touch_gui_button_id id,
@@ -363,6 +375,7 @@ void TouchControls::addButton(std::vector &buttons, touch_gui_butto
loadButtonTexture(btn_gui_button, image);
button_info &btn = buttons.emplace_back();
+ btn.id = id;
btn.keypress = id_to_keypress(id);
btn.gui_button = grab_gui_element(btn_gui_button);
}
@@ -429,7 +442,8 @@ void TouchControls::handleReleaseEvent(size_t pointer_id)
// If m_tap_state is already set to TapState::ShortTap, we must keep
// that value. Otherwise, many short taps will be ignored if you tap
// very fast.
- if (!m_move_has_really_moved && !m_move_prevent_short_tap &&
+ if (m_interaction_style != BUTTONS_CROSSHAIR &&
+ !m_move_has_really_moved && !m_move_prevent_short_tap &&
m_tap_state != TapState::LongTap) {
m_tap_state = TapState::ShortTap;
} else {
@@ -655,7 +669,9 @@ void TouchControls::step(float dtime)
applyJoystickStatus();
// if a new placed pointer isn't moved for some time start digging
- if (m_has_move_id && !m_move_has_really_moved && m_tap_state == TapState::None) {
+ if (m_interaction_style != BUTTONS_CROSSHAIR &&
+ m_has_move_id && !m_move_has_really_moved &&
+ m_tap_state == TapState::None) {
u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
if (delta > m_long_tap_delay) {
@@ -669,7 +685,7 @@ void TouchControls::step(float dtime)
// shootline when a touch event occurs.
// Only updating when m_has_move_id means that the shootline will stay at
// it's last in-world position when the player doesn't need it.
- if (!m_use_crosshair && (m_has_move_id || m_had_move_id)) {
+ if (m_interaction_style == TAP && (m_has_move_id || m_had_move_id)) {
m_shootline = m_device
->getSceneManager()
->getSceneCollisionManager()
@@ -757,6 +773,9 @@ void TouchControls::show()
void TouchControls::applyContextControls(const TouchInteractionMode &mode)
{
+ if (m_interaction_style == BUTTONS_CROSSHAIR)
+ return;
+
// Since the pointed thing has already been determined when this function
// is called, we cannot use this function to update the shootline.
diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h
index b64457ca5..c2ed8ae7b 100644
--- a/src/gui/touchcontrols.h
+++ b/src/gui/touchcontrols.h
@@ -57,6 +57,7 @@ enum class TapState
struct button_info
{
+ touch_gui_button_id id;
float repeat_counter;
KeyPress keypress;
std::vector pointer_ids;
@@ -94,7 +95,7 @@ public:
return res;
}
- bool isShootlineAvailable() { return !m_use_crosshair; }
+ bool isShootlineAvailable() { return m_interaction_style == TAP; }
/**
* Returns a line which describes what the player is pointing at.
@@ -137,7 +138,7 @@ private:
s32 m_button_size;
// cached settings
- bool m_use_crosshair;
+ TouchInteractionStyle m_interaction_style;
double m_touchscreen_threshold;
u16 m_long_tap_delay;
bool m_fixed_joystick;
@@ -161,7 +162,7 @@ private:
* The line ends on the camera's far plane.
* The coordinates do not contain the camera offset.
*
- * Only valid if !m_use_crosshair
+ * Only used for m_interaction_style == TAP
*/
line3d m_shootline;
@@ -243,6 +244,8 @@ private:
// map to store the IDs and positions of currently pressed pointers
std::unordered_map m_pointer_pos;
+ // The following are not used if m_interaction_style == BUTTONS_CROSSHAIR
+
TouchInteractionMode m_last_mode = TouchInteractionMode_END;
TapState m_tap_state = TapState::None;
diff --git a/src/gui/touchscreeneditor.cpp b/src/gui/touchscreeneditor.cpp
index 4a8468ee5..76403973f 100644
--- a/src/gui/touchscreeneditor.cpp
+++ b/src/gui/touchscreeneditor.cpp
@@ -371,7 +371,7 @@ bool GUITouchscreenLayout::OnEvent(const SEvent& event)
}
if (event.GUIEvent.Caller == m_gui_reset_btn.get()) {
- m_layout = ButtonLayout::predefined;
+ m_layout = ButtonLayout::loadDefault();
regenerateGui(screensize);
return true;
}
diff --git a/src/gui/touchscreenlayout.cpp b/src/gui/touchscreenlayout.cpp
index e0ad5b723..bd97526ff 100644
--- a/src/gui/touchscreenlayout.cpp
+++ b/src/gui/touchscreenlayout.cpp
@@ -12,6 +12,15 @@
#include "IGUIFont.h"
#include "IGUIStaticText.h"
+#include "util/enum_string.h"
+
+const struct EnumString es_TouchInteractionStyle[] =
+{
+ {TAP, "tap"},
+ {TAP_CROSSHAIR, "tap_crosshair"},
+ {BUTTONS_CROSSHAIR, "buttons_crosshair"},
+ {0, NULL},
+};
const char *button_names[] = {
"dig",
@@ -73,8 +82,8 @@ const char *button_titles[] = {
};
const char *button_image_names[] = {
- "",
- "",
+ "dig_btn.png",
+ "place_btn.png",
"jump_btn.png",
"down.png",
@@ -130,11 +139,21 @@ void ButtonMeta::setPos(v2s32 pos, v2u32 screensize, s32 button_size)
offset.Y = (pos.Y - (position.Y * screensize.Y)) / button_size;
}
+bool ButtonLayout::isButtonValid(touch_gui_button_id id)
+{
+ return id != joystick_off_id && id != joystick_bg_id && id != joystick_center_id &&
+ id < touch_gui_button_id_END;
+}
+
+static const char *buttons_crosshair = enum_to_string(es_TouchInteractionStyle, BUTTONS_CROSSHAIR);
+
bool ButtonLayout::isButtonAllowed(touch_gui_button_id id)
{
- return id != dig_id && id != place_id &&
- id != joystick_off_id && id != joystick_bg_id && id != joystick_center_id &&
- id != touch_gui_button_id_END;
+ if (id == dig_id || id == place_id)
+ return g_settings->get("touch_interaction_style") == buttons_crosshair;
+ if (id == aux1_id)
+ return !g_settings->getBool("virtual_joystick_triggers_aux1");
+ return true;
}
bool ButtonLayout::isButtonRequired(touch_gui_button_id id)
@@ -149,7 +168,15 @@ s32 ButtonLayout::getButtonSize(v2u32 screensize)
g_settings->getFloat("hud_scaling"));
}
-const ButtonLayout ButtonLayout::predefined {{
+const ButtonLayout::ButtonMap ButtonLayout::default_data {
+ {dig_id, {
+ v2f(1.0f, 1.0f),
+ v2f(-2.0f, -2.75f),
+ }},
+ {place_id, {
+ v2f(1.0f, 1.0f),
+ v2f(-2.0f, -4.25f),
+ }},
{jump_id, {
v2f(1.0f, 1.0f),
v2f(-1.0f, -0.5f),
@@ -170,28 +197,40 @@ const ButtonLayout ButtonLayout::predefined {{
v2f(1.0f, 1.0f),
v2f(-0.75f, -5.0f),
}},
-}};
+};
+
+ButtonLayout ButtonLayout::postProcessLoaded(const ButtonMap &data)
+{
+ ButtonLayout layout;
+ for (const auto &[id, meta] : data) {
+ assert(isButtonValid(id));
+ if (isButtonAllowed(id))
+ layout.layout.emplace(id, meta);
+ else
+ layout.preserved_disallowed.emplace(id, meta);
+ }
+ return layout;
+}
+
+ButtonLayout ButtonLayout::loadDefault()
+{
+ return postProcessLoaded(default_data);
+}
ButtonLayout ButtonLayout::loadFromSettings()
{
- bool restored = false;
- ButtonLayout layout;
-
std::string str = g_settings->get("touch_layout");
if (!str.empty()) {
std::istringstream iss(str);
try {
- layout.deserializeJson(iss);
- restored = true;
+ ButtonMap data = deserializeJson(iss);
+ return postProcessLoaded(data);
} catch (const Json::Exception &e) {
warningstream << "Could not parse touchscreen layout: " << e.what() << std::endl;
}
}
- if (!restored)
- return predefined;
-
- return layout;
+ return loadDefault();
}
std::unordered_map> ButtonLayout::texture_cache;
@@ -234,7 +273,7 @@ std::vector ButtonLayout::getMissingButtons()
std::vector missing_buttons;
for (u8 i = 0; i < touch_gui_button_id_END; i++) {
touch_gui_button_id btn = (touch_gui_button_id)i;
- if (isButtonAllowed(btn) && layout.count(btn) == 0)
+ if (isButtonValid(btn) && isButtonAllowed(btn) && layout.count(btn) == 0)
missing_buttons.push_back(btn);
}
return missing_buttons;
@@ -243,16 +282,19 @@ std::vector ButtonLayout::getMissingButtons()
void ButtonLayout::serializeJson(std::ostream &os) const
{
Json::Value root = Json::objectValue;
+ root["version"] = 1;
root["layout"] = Json::objectValue;
- for (const auto &[id, meta] : layout) {
- Json::Value button = Json::objectValue;
- button["position_x"] = meta.position.X;
- button["position_y"] = meta.position.Y;
- button["offset_x"] = meta.offset.X;
- button["offset_y"] = meta.offset.Y;
+ for (const auto &list : {layout, preserved_disallowed}) {
+ for (const auto &[id, meta] : list) {
+ Json::Value button = Json::objectValue;
+ button["position_x"] = meta.position.X;
+ button["position_y"] = meta.position.Y;
+ button["offset_x"] = meta.offset.X;
+ button["offset_y"] = meta.offset.Y;
- root["layout"][button_names[id]] = button;
+ root["layout"][button_names[id]] = button;
+ }
}
fastWriteJson(root, os);
@@ -267,13 +309,19 @@ static touch_gui_button_id button_name_to_id(const std::string &name)
return touch_gui_button_id_END;
}
-void ButtonLayout::deserializeJson(std::istream &is)
+ButtonLayout::ButtonMap ButtonLayout::deserializeJson(std::istream &is)
{
- layout.clear();
+ ButtonMap data;
Json::Value root;
is >> root;
+ u8 version;
+ if (root["version"].isUInt())
+ version = root["version"].asUInt();
+ else
+ version = 0;
+
if (!root["layout"].isObject())
throw Json::RuntimeError("invalid type for layout");
@@ -281,7 +329,7 @@ void ButtonLayout::deserializeJson(std::istream &is)
Json::ValueIterator iter;
for (iter = obj.begin(); iter != obj.end(); iter++) {
touch_gui_button_id id = button_name_to_id(iter.name());
- if (!isButtonAllowed(id))
+ if (!isButtonValid(id))
throw Json::RuntimeError("invalid button name");
Json::Value &value = *iter;
@@ -300,8 +348,20 @@ void ButtonLayout::deserializeJson(std::istream &is)
meta.offset.X = value["offset_x"].asFloat();
meta.offset.Y = value["offset_y"].asFloat();
- layout.emplace(id, meta);
+ data.emplace(id, meta);
}
+
+ if (version < 1) {
+ // Version 0 did not have dig/place buttons, so add them in.
+ // Otherwise, the missing buttons would cause confusion if the user
+ // switches to "touch_interaction_style = buttons_crosshair".
+ // This may result in overlapping buttons (could be fixed by resolving
+ // collisions in postProcessLoaded).
+ data.emplace(dig_id, default_data.at(dig_id));
+ data.emplace(place_id, default_data.at(place_id));
+ }
+
+ return data;
}
void layout_button_grid(v2u32 screensize, ISimpleTextureSource *tsrc,
diff --git a/src/gui/touchscreenlayout.h b/src/gui/touchscreenlayout.h
index fe0d99b9d..7a181d701 100644
--- a/src/gui/touchscreenlayout.h
+++ b/src/gui/touchscreenlayout.h
@@ -7,6 +7,7 @@
#include "irr_ptr.h"
#include "irrlichttypes_bloated.h"
#include "rect.h"
+#include "util/enum_string.h"
#include
#include
@@ -20,9 +21,16 @@ namespace irr::video
class ITexture;
}
+enum TouchInteractionStyle : u8
+{
+ TAP,
+ TAP_CROSSHAIR,
+ BUTTONS_CROSSHAIR,
+};
+extern const struct EnumString es_TouchInteractionStyle[];
+
enum touch_gui_button_id : u8
{
- // these two are no actual buttons ... yet
dig_id = 0,
place_id,
@@ -75,15 +83,34 @@ struct ButtonMeta {
};
struct ButtonLayout {
+ using ButtonMap = std::unordered_map;
+
+ static bool isButtonValid(touch_gui_button_id id);
static bool isButtonAllowed(touch_gui_button_id id);
static bool isButtonRequired(touch_gui_button_id id);
static s32 getButtonSize(v2u32 screensize);
+
+ // Returns the default layout.
+ // Note: Indirectly depends on settings.
+ static ButtonLayout loadDefault();
+ // Reads the layout from the "touch_layout" setting. Falls back to loadDefault
+ // if the setting is absent or invalid.
+ // Note: Indirectly depends on additional settings.
static ButtonLayout loadFromSettings();
static video::ITexture *getTexture(touch_gui_button_id btn, ISimpleTextureSource *tsrc);
static void clearTextureCache();
- std::unordered_map layout;
+ ButtonMap layout;
+
+ // Contains disallowed buttons that have been loaded.
+ // These are only preserved to be saved again later.
+ // This exists to prevent data loss: If you edit the layout while some button
+ // is temporarily disallowed, this prevents that button's custom position
+ // from being lost. See isButtonAllowed.
+ // This may result in overlapping buttons when the buttons are allowed again
+ // (could be fixed by resolving collisions in postProcessLoaded).
+ ButtonMap preserved_disallowed;
core::recti getRect(touch_gui_button_id btn,
v2u32 screensize, s32 button_size, ISimpleTextureSource *tsrc);
@@ -91,11 +118,12 @@ struct ButtonLayout {
std::vector getMissingButtons();
void serializeJson(std::ostream &os) const;
- void deserializeJson(std::istream &is);
-
- static const ButtonLayout predefined;
private:
+ static const ButtonMap default_data;
+ static ButtonMap deserializeJson(std::istream &is);
+ static ButtonLayout postProcessLoaded(const ButtonMap &map);
+
static std::unordered_map> texture_cache;
};
diff --git a/src/migratesettings.h b/src/migratesettings.h
index 5f6396914..4839f9479 100644
--- a/src/migratesettings.h
+++ b/src/migratesettings.h
@@ -28,4 +28,11 @@ void migrate_settings()
}
g_settings->remove("disable_anticheat");
}
+
+ // Convert touch_use_crosshair to touch_interaction_style
+ if (g_settings->existsLocal("touch_use_crosshair")) {
+ bool value = g_settings->getBool("touch_use_crosshair");
+ g_settings->set("touch_interaction_style", value ? "tap_crosshair" : "tap");
+ g_settings->remove("touch_use_crosshair");
+ }
}
diff --git a/textures/base/pack/dig_btn.png b/textures/base/pack/dig_btn.png
new file mode 100644
index 000000000..d612d1134
Binary files /dev/null and b/textures/base/pack/dig_btn.png differ
diff --git a/textures/base/pack/place_btn.png b/textures/base/pack/place_btn.png
new file mode 100644
index 000000000..f834e62c4
Binary files /dev/null and b/textures/base/pack/place_btn.png differ