1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-21 11:09:43 +00:00

Merge branch 'lua_ui_image' into 'master'

Document the Lua Image widget, add UI texture resources

See merge request OpenMW/openmw!1637
This commit is contained in:
psi29a 2022-02-26 17:48:50 +00:00
commit 2d4e9b38ae
16 changed files with 258 additions and 64 deletions

View file

@ -26,7 +26,10 @@
namespace MWLua namespace MWLua
{ {
LuaManager::LuaManager(const VFS::Manager* vfs, const std::string& libsDir) : mLua(vfs, &mConfiguration), mI18n(vfs, &mLua) LuaManager::LuaManager(const VFS::Manager* vfs, const std::string& libsDir)
: mLua(vfs, &mConfiguration)
, mUiResourceManager(vfs)
, mI18n(vfs, &mLua)
{ {
Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion(); Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion();
mLua.addInternalLibSearchPath(libsDir); mLua.addInternalLibSearchPath(libsDir);
@ -248,6 +251,7 @@ namespace MWLua
void LuaManager::clear() void LuaManager::clear()
{ {
LuaUi::clearUserInterface(); LuaUi::clearUserInterface();
mUiResourceManager.clear();
mActiveLocalScripts.clear(); mActiveLocalScripts.clear();
mLocalEvents.clear(); mLocalEvents.clear();
mGlobalEvents.clear(); mGlobalEvents.clear();
@ -470,7 +474,8 @@ namespace MWLua
{ {
Log(Debug::Info) << "Reload Lua"; Log(Debug::Info) << "Reload Lua";
LuaUi::clearUserInterface(); LuaUi::clearUserInterface();
mUiResourceManager.clear();
mLua.dropScriptCache(); mLua.dropScriptCache();
initConfiguration(); initConfiguration();

View file

@ -8,6 +8,8 @@
#include <components/lua/luastate.hpp> #include <components/lua/luastate.hpp>
#include <components/lua/storage.hpp> #include <components/lua/storage.hpp>
#include <components/lua_ui/resources.hpp>
#include "../mwbase/luamanager.hpp" #include "../mwbase/luamanager.hpp"
#include "actions.hpp" #include "actions.hpp"
@ -89,6 +91,8 @@ namespace MWLua
return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); }; return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); };
} }
LuaUi::ResourceManager* uiResourceManager() { return &mUiResourceManager; }
private: private:
void initConfiguration(); void initConfiguration();
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScriptCfg::Flags); LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr, ESM::LuaScriptCfg::Flags);
@ -97,6 +101,7 @@ namespace MWLua
bool mGlobalScriptsStarted = false; bool mGlobalScriptsStarted = false;
LuaUtil::ScriptsConfiguration mConfiguration; LuaUtil::ScriptsConfiguration mConfiguration;
LuaUtil::LuaState mLua; LuaUtil::LuaState mLua;
LuaUi::ResourceManager mUiResourceManager;
LuaUtil::I18nManager mI18n; LuaUtil::I18nManager mI18n;
sol::table mNearbyPackage; sol::table mNearbyPackage;
sol::table mUserInterfacePackage; sol::table mUserInterfacePackage;

View file

@ -4,6 +4,7 @@
#include <components/lua_ui/content.hpp> #include <components/lua_ui/content.hpp>
#include <components/lua_ui/registerscriptsettings.hpp> #include <components/lua_ui/registerscriptsettings.hpp>
#include <components/lua_ui/alignment.hpp> #include <components/lua_ui/alignment.hpp>
#include <components/lua_ui/resources.hpp>
#include "context.hpp" #include "context.hpp"
#include "actions.hpp" #include "actions.hpp"
@ -77,46 +78,46 @@ namespace MWLua
std::shared_ptr<LuaUi::Element> mElement; std::shared_ptr<LuaUi::Element> mElement;
}; };
class InsertLayerAction final : public Action
{
public:
InsertLayerAction(std::string_view name, std::string_view afterName,
LuaUi::Layers::Options options, LuaUtil::LuaState* state)
: Action(state)
, mName(name)
, mAfterName(afterName)
, mOptions(options)
{}
void apply(WorldView&) const override
{
size_t index = LuaUi::Layers::indexOf(mAfterName);
if (index == LuaUi::Layers::size())
throw std::logic_error(std::string("Layer not found"));
LuaUi::Layers::insert(index, mName, mOptions);
}
std::string toString() const override
{
std::string result("Insert UI layer \"");
result += mName;
result += "\" after \"";
result += mAfterName;
result += "\"";
return result;
}
private:
std::string mName;
std::string mAfterName;
LuaUi::Layers::Options mOptions;
};
// Lua arrays index from 1 // Lua arrays index from 1
inline size_t fromLuaIndex(size_t i) { return i - 1; } inline size_t fromLuaIndex(size_t i) { return i - 1; }
inline size_t toLuaIndex(size_t i) { return i + 1; } inline size_t toLuaIndex(size_t i) { return i + 1; }
} }
class LayerAction final : public Action
{
public:
LayerAction(std::string_view name, std::string_view afterName,
LuaUi::Layers::Options options, LuaUtil::LuaState* state)
: Action(state)
, mName(name)
, mAfterName(afterName)
, mOptions(options)
{}
void apply(WorldView&) const override
{
size_t index = LuaUi::Layers::indexOf(mAfterName);
if (index == LuaUi::Layers::size())
throw std::logic_error(std::string("Layer not found"));
LuaUi::Layers::insert(index, mName, mOptions);
}
std::string toString() const override
{
std::string result("Insert UI layer \"");
result += mName;
result += "\" after \"";
result += mAfterName;
result += "\"";
return result;
}
private:
std::string mName;
std::string mAfterName;
LuaUi::Layers::Options mOptions;
};
sol::table initUserInterfacePackage(const Context& context) sol::table initUserInterfacePackage(const Context& context)
{ {
auto uiContent = context.mLua->sol().new_usertype<LuaUi::Content>("UiContent"); auto uiContent = context.mLua->sol().new_usertype<LuaUi::Content>("UiContent");
@ -246,7 +247,7 @@ namespace MWLua
{ {
LuaUi::Layers::Options options; LuaUi::Layers::Options options;
options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true); options.mInteractive = LuaUtil::getValueOrDefault(LuaUtil::getFieldOrNil(opt, "interactive"), true);
context.mLuaManager->addAction(std::make_unique<LayerAction>(name, afterName, options, context.mLua)); context.mLuaManager->addAction(std::make_unique<InsertLayerAction>(name, afterName, options, context.mLua));
}; };
{ {
auto pairs = [layers](const sol::object&) auto pairs = [layers](const sol::object&)
@ -278,6 +279,23 @@ namespace MWLua
api["registerSettingsPage"] = &LuaUi::registerSettingsPage; api["registerSettingsPage"] = &LuaUi::registerSettingsPage;
api["texture"] = [luaManager=context.mLuaManager](const sol::table& options)
{
LuaUi::TextureData data;
sol::object path = LuaUtil::getFieldOrNil(options, "path");
if (path.is<std::string>())
data.mPath = path.as<std::string>();
if (data.mPath.empty())
throw std::logic_error("Invalid texture path");
sol::object offset = LuaUtil::getFieldOrNil(options, "offset");
if (offset.is<osg::Vec2f>())
data.mOffset = offset.as<osg::Vec2f>();
sol::object size = LuaUtil::getFieldOrNil(options, "size");
if (size.is<osg::Vec2f>())
data.mSize = size.as<osg::Vec2f>();
return luaManager->uiResourceManager()->registerTexture(data);
};
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);
} }
} }

View file

@ -259,7 +259,7 @@ add_component_dir (queries
add_component_dir (lua_ui add_component_dir (lua_ui
registerscriptsettings scriptsettings registerscriptsettings scriptsettings
properties widget element util layers content alignment properties widget element util layers content alignment resources
adapter text textedit window image container adapter text textedit window image container
) )

View file

@ -130,8 +130,7 @@ namespace LuaUi
void updateWidget(WidgetExtension* ext, const sol::table& layout) void updateWidget(WidgetExtension* ext, const sol::table& layout)
{ {
ext->resetSlot(); // otherwise if template gets changed, all non-template children will get destroyed ext->reset();
ext->setLayout(layout); ext->setLayout(layout);
ext->setExternal(layout.get<sol::object>(LayoutKeys::external)); ext->setExternal(layout.get<sol::object>(LayoutKeys::external));
setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout)); setTemplate(ext, layout.get<sol::object>(LayoutKeys::templateLayout));

View file

@ -2,6 +2,8 @@
#include <MyGUI_RenderManager.h> #include <MyGUI_RenderManager.h>
#include "resources.hpp"
namespace LuaUi namespace LuaUi
{ {
void LuaTileRect::_setAlign(const MyGUI::IntSize& _oldsize) void LuaTileRect::_setAlign(const MyGUI::IntSize& _oldsize)
@ -25,7 +27,7 @@ namespace LuaUi
mTileSize.height = 1e7; mTileSize.height = 1e7;
} }
LuaImage::LuaImage() void LuaImage::initialize()
{ {
changeWidgetSkin("LuaImage"); changeWidgetSkin("LuaImage");
mTileRect = dynamic_cast<LuaTileRect*>(getSubWidgetMain()); mTileRect = dynamic_cast<LuaTileRect*>(getSubWidgetMain());
@ -33,19 +35,37 @@ namespace LuaUi
void LuaImage::updateProperties() void LuaImage::updateProperties()
{ {
setImageTexture(propertyValue("path", std::string())); TextureResource* resource = propertyValue<TextureResource*>("resource", nullptr);
MyGUI::IntCoord atlasCoord;
if (resource)
{
atlasCoord = MyGUI::IntCoord(
static_cast<int>(resource->mOffset.x()),
static_cast<int>(resource->mOffset.y()),
static_cast<int>(resource->mSize.x()),
static_cast<int>(resource->mSize.y()));
setImageTexture(resource->mPath);
}
bool tileH = propertyValue("tileH", false); bool tileH = propertyValue("tileH", false);
bool tileV = propertyValue("tileV", false); bool tileV = propertyValue("tileV", false);
MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(_getTextureName()); MyGUI::ITexture* texture = MyGUI::RenderManager::getInstance().getTexture(_getTextureName());
MyGUI::IntSize textureSize; MyGUI::IntSize textureSize;
if (texture != nullptr) if (texture != nullptr)
textureSize = MyGUI::IntSize(texture->getWidth(), texture->getHeight()); textureSize = MyGUI::IntSize(texture->getWidth(), texture->getHeight());
mTileRect->updateSize(MyGUI::IntSize( mTileRect->updateSize(MyGUI::IntSize(
tileH ? textureSize.width : 0, tileH ? textureSize.width : 0,
tileV ? textureSize.height : 0 tileV ? textureSize.height : 0
)); ));
if (atlasCoord.width == 0)
atlasCoord.width = textureSize.width;
if (atlasCoord.height == 0)
atlasCoord.height = textureSize.height;
setImageCoord(atlasCoord);
WidgetExtension::updateProperties(); WidgetExtension::updateProperties();
} }
} }

View file

@ -25,10 +25,8 @@ namespace LuaUi
{ {
MYGUI_RTTI_DERIVED(LuaImage) MYGUI_RTTI_DERIVED(LuaImage)
public:
LuaImage();
protected: protected:
void initialize() override;
void updateProperties() override; void updateProperties() override;
LuaTileRect* mTileRect; LuaTileRect* mTileRect;
}; };

View file

@ -0,0 +1,27 @@
#include "resources.hpp"
#include <components/vfs/manager.hpp>
#include <components/misc/stringops.hpp>
namespace LuaUi
{
std::shared_ptr<TextureResource> ResourceManager::registerTexture(TextureData data)
{
std::string normalizedPath = mVfs->normalizeFilename(data.mPath);
if (!mVfs->exists(normalizedPath))
{
auto error = Misc::StringUtils::format("Texture with path \"%s\" doesn't exist", data.mPath);
throw std::logic_error(error);
}
data.mPath = normalizedPath;
TextureResources& list = mTextures[normalizedPath];
list.push_back(std::make_shared<TextureResource>(data));
return list.back();
}
void ResourceManager::clear()
{
mTextures.clear();
}
}

View file

@ -0,0 +1,45 @@
#ifndef OPENMW_LUAUI_RESOURCES
#define OPENMW_LUAUI_RESOURCES
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
#include <osg/Vec2f>
namespace VFS
{
class Manager;
}
namespace LuaUi
{
struct TextureData
{
std::string mPath;
osg::Vec2f mOffset;
osg::Vec2f mSize;
};
// will have more/different data when automated atlasing is supported
using TextureResource = TextureData;
class ResourceManager
{
public:
ResourceManager(const VFS::Manager* vfs)
: mVfs(vfs)
{}
std::shared_ptr<TextureResource> registerTexture(TextureData data);
void clear();
private:
const VFS::Manager* mVfs;
using TextureResources = std::vector<std::shared_ptr<TextureResource>>;
std::unordered_map<std::string, TextureResources> mTextures;
};
}
#endif // OPENMW_LUAUI_LAYERS

View file

@ -10,7 +10,7 @@ namespace LuaUi
void LuaText::initialize() void LuaText::initialize()
{ {
changeWidgetSkin("SandText"); changeWidgetSkin("LuaText");
setEditStatic(true); setEditStatic(true);
setVisibleHScroll(false); setVisibleHScroll(false);
setVisibleVScroll(false); setVisibleVScroll(false);

View file

@ -24,9 +24,8 @@ namespace LuaUi
{ {
mLua = lua; mLua = lua;
mWidget = self; mWidget = self;
updateTemplate();
initialize(); initialize();
updateTemplate();
} }
void WidgetExtension::initialize() void WidgetExtension::initialize()
@ -72,6 +71,13 @@ namespace LuaUi
w->deinitialize(); w->deinitialize();
} }
void WidgetExtension::reset()
{
// detach all children from the slot widget, in case it gets destroyed
for (auto& w: mChildren)
w->widget()->detachFromWidget();
}
void WidgetExtension::attach(WidgetExtension* ext) void WidgetExtension::attach(WidgetExtension* ext)
{ {
ext->mParent = this; ext->mParent = this;
@ -182,8 +188,15 @@ namespace LuaUi
else else
mSlot = slot->mSlot; mSlot = slot->mSlot;
if (mSlot != oldSlot) if (mSlot != oldSlot)
for (WidgetExtension* w : mChildren) {
attach(w); MyGUI::IntSize slotSize = mSlot->widget()->getSize();
MyGUI::IntPoint slotPosition = mSlot->widget()->getAbsolutePosition() - widget()->getAbsolutePosition();
MyGUI::IntCoord slotCoord(slotPosition, slotSize);
if (mWidget->getSubWidgetMain())
mWidget->getSubWidgetMain()->setCoord(slotCoord);
if (mWidget->getSubWidgetText())
mWidget->getSubWidgetText()->setCoord(slotCoord);
}
} }
void WidgetExtension::setCallback(const std::string& name, const LuaUtil::Callback& callback) void WidgetExtension::setCallback(const std::string& name, const LuaUtil::Callback& callback)

View file

@ -28,6 +28,9 @@ namespace LuaUi
virtual void deinitialize(); virtual void deinitialize();
MyGUI::Widget* widget() const { return mWidget; } MyGUI::Widget* widget() const { return mWidget; }
WidgetExtension* slot() const { return mSlot; }
void reset();
const std::vector<WidgetExtension*>& children() { return mChildren; } const std::vector<WidgetExtension*>& children() { return mChildren; }
void setChildren(const std::vector<WidgetExtension*>&); void setChildren(const std::vector<WidgetExtension*>&);
@ -50,7 +53,6 @@ namespace LuaUi
const sol::table& getLayout() { return mLayout; } const sol::table& getLayout() { return mLayout; }
void setLayout(const sol::table& layout) { mLayout = layout; } void setLayout(const sol::table& layout) { mLayout = layout; }
void resetSlot() { mSlot = this; }
template <typename T> template <typename T>
T externalValue(std::string_view name, const T& defaultValue) T externalValue(std::string_view name, const T& defaultValue)

View file

@ -80,6 +80,7 @@ Widget types
Widget: Base widget type, all the other widgets inherit its properties and events. <widgets/widget> Widget: Base widget type, all the other widgets inherit its properties and events. <widgets/widget>
Text: Displays text. <widgets/text> Text: Displays text. <widgets/text>
TextEdit: Accepts text input from the user. <widgets/textedit> TextEdit: Accepts text input from the user. <widgets/textedit>
Image: Renders a texture. <widgets/image>
Example Example
------- -------

View file

@ -0,0 +1,22 @@
Image Widget
============
Properties
----------
.. list-table::
:header-rows: 1
:widths: 20 20 60
* - name
- type (default value)
- description
* - resource
- ui.texture
- The texture resource to display
* - tileH
- boolean (false)
- Tile the texture horizontally
* - tileV
- boolean (false)
- Tile the texture vertically

View file

@ -53,7 +53,15 @@
--- ---
-- Adds a settings page to main menu setting's Scripts tab. -- Adds a settings page to main menu setting's Scripts tab.
-- @function [parent=#ui] registerSettingsPage -- @function [parent=#ui] registerSettingsPage
-- @param #SettingsPage page -- @param #SettingsPageOptions page
---
-- Table with settings page options, passed as an argument to ui.registerSettingsPage
-- @type SettingsPageOptions
-- @field #string name Name of the page, displayed in the list, used for search
-- @field #string searchHints Additional keywords used in search, not displayed anywhere
-- @field #Element element The page's UI, which will be attached to the settings tab. The root widget has to have a fixed size. Set the `size` field in `props`, `relativeSize` is ignored.
--- ---
-- Layout -- Layout
@ -162,10 +170,33 @@
-- @field #number button Mouse button which triggered the event (could be nil) -- @field #number button Mouse button which triggered the event (could be nil)
--- ---
-- Settings page parameters, passed as an argument to ui.registerSettingsPage -- Register a new texture resource. Can be used to manually atlas UI textures.
-- @type SettingsPage -- @function [parent=#ui] texture #TextureResource
-- @field #string name Name of the page, displayed in the list, used for search -- @param #TextureResourceOptions options
-- @field #string searchHints Additional keywords used in search, not displayed anywhere -- @usage
-- @field #Element element The page's UI, which will be attached to the settings tab. The root widget has to have a fixed size (set `size` field in `props`, see Widget documentation, `relativeSize` is ignored) -- local ui = require('openmw.ui')
-- local vector2 = require('openmw.util').vector2
-- local myAtlas = 'textures/my_atlas.dds' -- a 128x128 atlas
-- local texture1 = ui.texture { -- texture in the top left corner of the atlas
-- path = myAtlas,
-- offset = vector2(0, 0),
-- size = vector2(64, 64),
-- }
-- local texture2 = ui.texture { -- texture in the top right corner of the atlas
-- path = myAtlas,
-- offset = vector2(64, 0),
-- size = vector2(64, 64),
-- }
---
-- A texture ready to be used by UI widgets
-- @type TextureResource
---
-- Table with arguments passed to ui.texture.
-- @type TextureResourceOptions
-- @field #string path Path to the texture file. Required
-- @field openmw.util#Vector2 offset Offset of this resource in the texture. (0, 0) by default
-- @field openmw.util#Vector2 size Size of the resource in the texture. (0, 0) by default. 0 means the whole texture size is used.
return nil return nil

View file

@ -4,12 +4,20 @@
<Resource type="ResourceSkin" name="LuaImage"> <Resource type="ResourceSkin" name="LuaImage">
<BasisSkin type="LuaTileRect"/> <BasisSkin type="LuaTileRect"/>
</Resource> </Resource>
<Resource type="ResourceSkin" name="LuaTextEdit" size="512 20">
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Left VCenter"/>
<Property key="TextColour" value="#{fontcolour=normal}"/>
<Child type="TextBox" skin="MW_TextEditClient" offset="0 0 502 20" align="Stretch" name="Client"/> <Resource type="ResourceSkin" name="LuaText" size="16 16">
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Left Top"/>
<BasisSkin type="SimpleText" offset="0 0 16 16" align="Stretch" name="Client" />
</Resource>
<Resource type="ResourceSkin" name="LuaTextEditClient" size="16 16">
<BasisSkin type="EditText" offset="0 0 16 16" align="Stretch" name="Client" />
</Resource>
<Resource type="ResourceSkin" name="LuaTextEdit" size="16 16">
<Property key="FontName" value="Default"/>
<Property key="TextAlign" value="Left Top"/>
<Child type="TextBox" skin="LuaTextEditClient" offset="0 0 16 16" align="Stretch" name="Client"/>
</Resource> </Resource>
</MyGUI> </MyGUI>