[LuaGUI] Text input definition added.

[creative_inventory] Now has a search bar!
master
Quentin Bazin 2020-07-28 19:10:02 +02:00
parent 4337868fde
commit ef5ac1d818
16 changed files with 317 additions and 26 deletions

View File

@ -19,6 +19,7 @@
- [Inventory widget](lua-api-gui-inventory.md)
- [Progress bar](lua-api-gui-progress-bar.md)
- [Scroll bar](lua-api-gui-scroll-bar.md)
- [Text input](lua-api-gui-text-input.md)
# Misc

View File

@ -28,7 +28,7 @@ gui:inventory {
Use a group as a filter for this inventory widget.
Example:
```
```lua
filter = "group:om_fuel"
```

View File

@ -0,0 +1,91 @@
# Lua API: Text Input
**Note:** This only purpose of this atm is to filter items in an inventory widget. More uses will come later.
## Example
```lua
gui:text_input {
name = "inv_filter",
pos = {x = 82, y = 6},
width = 86,
height = 8,
placeholder = "Search...",
placeholder_color = {192, 192, 192},
inventory = "inv_creative_items"
}
```
## Attributes
### `height`
Height of the widget. **Mandatory field.**
Example:
```lua
height = 100
```
### `inventory`
Name of the inventory widget to filter.
Example:
```lua
inventory = "inv_blocks"
```
### `name`
Name of the widget. **Mandatory field.**
Example:
```lua
name = "inv_main"
```
### `placeholder`
Placeholder text displayed when the box is empty.
Example:
```lua
placeholder = "Search..."
```
### `placeholder_color`
Placeholder text color.
Example:
```lua
placeholder_color = {192, 192, 192}
```
**Note:** Alpha is optional, defaults to max value (`255`).
### `pos`
Position of the widget. **Mandatory field.**
Example:
```lua
pos = {
x = 0,
y = 0
}
```
### `width`
Width of the widget. **Mandatory field.**
Example:
```lua
width = 100
```

View File

@ -24,6 +24,7 @@ nav:
- 'Inventory widget': 'lua-api-gui-inventory.md'
- 'Progress bar': 'lua-api-gui-progress-bar.md'
- 'Scroll bar': 'lua-api-gui-scroll-bar.md'
- 'Text input': 'lua-api-gui-text-input.md'
- 'Misc':
- 'Network Protocol': 'network-protocol.md'

View File

@ -91,6 +91,19 @@ mod:key {
size = {x = 9, y = 7}
}
gui:text_input {
name = "inv_input_filter",
pos = {x = 82, y = 6},
width = 86,
height = 8,
placeholder = "Search...",
placeholder_color = {192, 192, 192},
inventory = "inv_creative_items"
}
gui:scroll_bar {
name = "scroll_bar",
pos = {x = 175, y = 18},

View File

@ -79,6 +79,13 @@ void InventoryWidget::update() {
sendUpdatePacket();
}
void InventoryWidget::applySearch(const std::string &search) {
if (search != m_lastSearch) {
loadItemWidgets(m_offset, m_size, search);
m_lastSearch = search;
}
}
bool InventoryWidget::sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) {
if (dest->doItemMatchFilter(itemStack->stack().item())) {
ItemStack stackRet = dest->receiveItemStack(itemStack, this);
@ -128,15 +135,25 @@ void InventoryWidget::draw(gk::RenderTarget &target, gk::RenderStates states) co
target.draw(m_selectedItemBackground, states);
}
void InventoryWidget::loadItemWidgets(u16 offset, u16 size) {
void InventoryWidget::loadItemWidgets(u16 offset, u16 size, std::string search) {
m_itemWidgets.clear();
u16 itemCounter = 0;
for (u16 i = 0 ; i < size ; ++i) {
m_itemWidgets.emplace_back(*m_inventory, (i + offset) % m_inventory->width(), (i + offset) / m_inventory->width(), this);
u16 x = (i + offset) % m_inventory->width();
u16 y = (i + offset) / m_inventory->width();
std::string label = m_inventory->getStack(x, y).item().label();
std::transform(label.begin(), label.end(), label.begin(), [](unsigned char c) { return std::tolower(c); });
std::transform(search.begin(), search.end(), search.begin(), [](unsigned char c) { return std::tolower(c); });
if (search.empty() || label.find(search) != std::string::npos) {
m_itemWidgets.emplace_back(*m_inventory, x, y, this);
ItemWidget &widget = m_itemWidgets.back();
widget.update();
widget.setPosition((i % m_inventory->width()) * 18, (i / m_inventory->width()) * 18, 0);
ItemWidget &widget = m_itemWidgets.back();
widget.update();
widget.setPosition((itemCounter % m_inventory->width()) * 18, (itemCounter / m_inventory->width()) * 18, 0);
itemCounter++;
}
}
}

View File

@ -47,6 +47,8 @@ class InventoryWidget : public AbstractInventoryWidget {
void update() override;
void applySearch(const std::string &search);
bool sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) override;
ItemStack receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) override;
@ -59,7 +61,7 @@ class InventoryWidget : public AbstractInventoryWidget {
private:
void draw(gk::RenderTarget &target, gk::RenderStates states) const override;
void loadItemWidgets(u16 offset, u16 size);
void loadItemWidgets(u16 offset, u16 size, std::string search = "");
ClientCommandHandler &m_client;
@ -75,6 +77,8 @@ class InventoryWidget : public AbstractInventoryWidget {
ItemWidget *m_currentItemWidget = nullptr;
gk::RectangleShape m_selectedItemBackground{16, 16, gk::Color{255, 255, 255, 80}};
std::string m_lastSearch;
};
#endif // INVENTORYWIDGET_HPP_

View File

@ -28,7 +28,7 @@
#include "TextInput.hpp"
TextInput::TextInput() {
TextInput::TextInput(Widget *parent) : Widget(parent) {
m_cursor.setString("_");
m_placeholder.setColor(gk::Color(150, 150, 150));
@ -36,14 +36,7 @@ TextInput::TextInput() {
void TextInput::onEvent(const SDL_Event &event) {
if (event.type == SDL_MOUSEBUTTONDOWN) {
gk::FloatRect rect{
getPosition().x,
getPosition().y,
getBackgroundSize().x * getScale().x,
getBackgroundSize().y * getScale().y
};
m_hasFocus = rect.contains(event.button.x, event.button.y);
m_hasFocus = isPointInWidget(event.button.x, event.button.y);
}
else if (m_hasFocus) {
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_BACKSPACE && !m_content.empty()) {

View File

@ -30,12 +30,13 @@
#include <gk/core/SDLHeaders.hpp>
#include "Text.hpp"
#include "Widget.hpp"
class TextInput : public gk::Drawable, public gk::Transformable {
class TextInput : public Widget {
public:
TextInput();
TextInput(Widget *parent = nullptr);
void onEvent(const SDL_Event &event);
void onEvent(const SDL_Event &event) override;
const std::string &string() const { return m_content; }
void setString(const std::string &string);
@ -43,14 +44,20 @@ class TextInput : public gk::Drawable, public gk::Transformable {
gk::Vector2f getBackgroundSize() const { return m_text.getBackgroundSize(); }
void setBackgroundColor(const gk::Color &color) { m_text.setBackgroundColor(color); }
void setBackgroundSize(unsigned int width, unsigned int height) { m_text.setBackgroundSize(width, height); }
void setBackgroundOutline(int thickness, const gk::Color &color) { m_text.setBackgroundOutline(thickness, color); }
void setBackgroundSize(unsigned int width, unsigned int height) {
m_text.setBackgroundSize(width, height);
m_width = width;
m_height = height;
}
void setPadding(int x, int y) { m_text.setPadding(x, y); m_cursor.setPadding(x, y); m_placeholder.setPadding(x, y); }
void setCharacterLimit(u16 characterLimit) { m_characterLimit = characterLimit; }
void setPlaceholder(const std::string &placeholder) { m_placeholder.setString(placeholder); }
void setPlaceholderColor(const gk::Color &color) { m_placeholder.setColor(color); }
bool hasFocus() const { return m_hasFocus; }
void setFocus(bool hasFocus) { m_hasFocus = hasFocus; }
private:

View File

@ -77,13 +77,25 @@ void LuaGUIState::onEvent(const SDL_Event &event) {
if (event.type == SDL_KEYDOWN && (event.key.keysym.sym == SDLK_ESCAPE
|| (m_keyID >= 0 && event.key.keysym.sym == Registry::getInstance().getKey(m_keyID).keycode()))) {
gk::Mouse::setCursorGrabbed(true);
gk::Mouse::setCursorVisible(false);
gk::Mouse::resetToWindowCenter();
bool ignoreExit = false;
for (auto &it : m_textInputs) {
if (it.second.hasFocus()) {
ignoreExit = true;
isActive = false;
if (event.key.keysym.sym == SDLK_ESCAPE)
it.second.setFocus(false);
}
}
m_stateStack->pop();
if (!ignoreExit) {
gk::Mouse::setCursorGrabbed(true);
gk::Mouse::setCursorVisible(false);
gk::Mouse::resetToWindowCenter();
isActive = false;
m_stateStack->pop();
}
}
for (auto &it : m_widgets)
@ -123,11 +135,23 @@ void LuaGUIState::onEvent(const SDL_Event &event) {
m_mouseItemWidget.onEvent(event);
}
for (auto &it : m_textInputs)
it.second.onEvent(event);
}
void LuaGUIState::update() {
InterfaceState::update();
for (auto &it : m_textInputInventories) {
auto inv = m_inventoryWidgets.find(it.second);
if (inv != m_inventoryWidgets.end()) {
inv->second.applySearch(m_textInputs.at(it.first).string());
}
else
gkError() << "Can't find linked inventory" << it.second << "for text input" << it.first;
}
for (auto &it : m_widgets)
it->update();
@ -177,6 +201,9 @@ void LuaGUIState::draw(gk::RenderTarget &target, gk::RenderStates states) const
for (auto &it : m_craftingWidgets)
target.draw(it.second, states);
for (auto &it : m_textInputs)
target.draw(it.second, states);
target.draw(m_mouseItemWidget, states);
}
@ -201,6 +228,8 @@ void LuaGUIState::loadGUI(sf::Packet &packet) {
loadCraftingWidget(name, x, y, packet);
else if (type == LuaWidget::ScrollBarWidget)
loadScrollBarWidget(name, x, y, packet);
else if (type == LuaWidget::TextInput)
loadTextInput(name, x, y, packet);
else if (type == LuaWidget::Inventory)
loadInventory(name, packet);
}
@ -370,6 +399,23 @@ void LuaGUIState::loadScrollBarWidget(const std::string &, s32 x, s32 y, sf::Pac
m_widgets.emplace_back(scrollBarWidget);
}
void LuaGUIState::loadTextInput(const std::string &name, s32 x, s32 y, sf::Packet &packet) {
u16 width, height;
std::string placeholder, inventory;
gk::Color placeholderColor;
packet >> width >> height >> placeholder >> placeholderColor >> inventory;
TextInput textInput{&m_mainWidget};
textInput.setPosition(x, y);
textInput.setFocus(false);
textInput.setBackgroundSize(width, height);
textInput.setPlaceholder(placeholder);
textInput.setPlaceholderColor(placeholderColor);
m_textInputs.emplace(name, std::move(textInput));
m_textInputInventories.emplace(name, inventory);
}
void LuaGUIState::loadInventory(const std::string &name, sf::Packet &packet) {
m_inventories.emplace(name, Inventory{});

View File

@ -36,6 +36,7 @@
#include "InterfaceState.hpp"
#include "InventoryWidget.hpp"
#include "MouseItemWidget.hpp"
#include "TextInput.hpp"
class ClientPlayer;
class ClientWorld;
@ -60,6 +61,7 @@ class LuaGUIState : public InterfaceState {
void loadCraftingWidget(const std::string &name, s32 x, s32 y, sf::Packet &packet);
void loadProgressBarWidget(const std::string &name, s32 x, s32 y, sf::Packet &packet);
void loadScrollBarWidget(const std::string &name, s32 x, s32 y, sf::Packet &packet);
void loadTextInput(const std::string &name, s32 x, s32 y, sf::Packet &packet);
void loadInventory(const std::string &name, sf::Packet &packet);
gk::Texture &loadTexture(const std::string &textureFilename);
@ -79,6 +81,8 @@ class LuaGUIState : public InterfaceState {
std::vector<std::unique_ptr<gk::Drawable>> m_drawables;
std::unordered_map<std::string, Inventory> m_inventories;
std::unordered_map<std::string, gk::Texture> m_textures;
std::unordered_map<std::string, TextInput> m_textInputs;
std::unordered_map<std::string, std::string> m_textInputInventories;
ClientPlayer &m_player;
ClientWorld &m_world;

View File

@ -39,7 +39,8 @@ namespace LuaWidget {
CraftingWidget = 4,
ProgressBarWidget = 5,
ScrollBarWidget = 6,
Inventory = 7,
TextInput = 7,
Inventory = 8,
};
}

View File

@ -0,0 +1,55 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner 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.
*
* OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include "TextInputWidgetDef.hpp"
void TextInputWidgetDef::serialize(sf::Packet &packet) const {
WidgetDef::serialize(packet);
packet << m_width << m_height << m_placeholder << m_placeholderColor << m_inventory;
}
void TextInputWidgetDef::loadFromLuaTable(const sol::table &table) {
WidgetDef::loadFromLuaTable(table);
m_width = table["width"].get<u16>();
m_height = table["height"].get<u16>();
m_placeholder = table["placeholder"].get_or<std::string>("");
sol::optional<sol::table> placeholderColor = table["placeholder_color"];
if (placeholderColor != sol::nullopt) {
m_placeholderColor = gk::Color{
placeholderColor.value().get<u8>(1),
placeholderColor.value().get<u8>(2),
placeholderColor.value().get<u8>(3),
placeholderColor.value().get_or<u8>(4, 255)
};
}
m_inventory = table["inventory"].get<std::string>();
}

View File

@ -0,0 +1,50 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner 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.
*
* OpenMiner 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 OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef TEXTINPUTWIDGETDEF_HPP_
#define TEXTINPUTWIDGETDEF_HPP_
#include "WidgetDef.hpp"
class TextInputWidgetDef : public WidgetDef {
public:
TextInputWidgetDef() : WidgetDef(LuaWidget::TextInput) {}
void serialize(sf::Packet &packet) const override;
void loadFromLuaTable(const sol::table &table) override;
private:
u16 m_width = 0;
u16 m_height = 0;
std::string m_placeholder;
gk::Color m_placeholderColor;
std::string m_inventory;
};
#endif // TEXTINPUTWIDGETDEF_HPP_

View File

@ -38,6 +38,7 @@
#include "ProgressBarWidgetDef.hpp"
#include "ScrollBarWidgetDef.hpp"
#include "TextButtonWidgetDef.hpp"
#include "TextInputWidgetDef.hpp"
void LuaGUI::addImage(const sol::table &table) {
m_widgetDefinitions.emplace_back(new ImageWidgetDef);
@ -69,6 +70,11 @@ void LuaGUI::addScrollBarWidget(const sol::table &table) {
m_widgetDefinitions.back()->loadFromLuaTable(table);
}
void LuaGUI::addTextInput(const sol::table &table) {
m_widgetDefinitions.emplace_back(new TextInputWidgetDef);
m_widgetDefinitions.back()->loadFromLuaTable(table);
}
void LuaGUI::addInventory(const sol::table &table) {
std::string name = table["name"].get<std::string>();
@ -116,6 +122,7 @@ void LuaGUI::initUsertype(sol::state &lua) {
"crafting", &LuaGUI::addCraftingWidget,
"progress_bar", &LuaGUI::addProgressBarWidget,
"scroll_bar", &LuaGUI::addScrollBarWidget,
"text_input", &LuaGUI::addTextInput,
"inventory_data", &LuaGUI::addInventory,
"set_size", &LuaGUI::setSize,

View File

@ -46,6 +46,7 @@ class LuaGUI {
void addCraftingWidget(const sol::table &table);
void addProgressBarWidget(const sol::table &table);
void addScrollBarWidget(const sol::table &table);
void addTextInput(const sol::table &table);
void addInventory(const sol::table &table);
void setSize(u16 width, u16 height) { m_width = width; m_height = height; }