diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2026-05-06 13:56:12 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-06 13:56:12 +0100 |
| commit | 1681bea42dd2f11ba3fe6df05560d0b231de3c76 (patch) | |
| tree | 899103dbddf675a01758c7cdd379224c9c81a7fb /src/config/lua/layout/LuaLayoutContext.cpp | |
| parent | 5f1350f522d3fe3af78cbe30a98311763b3509d5 (diff) | |
config/lua: add simple layout API (#14258)
Adds a simple layout lua api
Diffstat (limited to 'src/config/lua/layout/LuaLayoutContext.cpp')
| -rw-r--r-- | src/config/lua/layout/LuaLayoutContext.cpp | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/src/config/lua/layout/LuaLayoutContext.cpp b/src/config/lua/layout/LuaLayoutContext.cpp new file mode 100644 index 00000000..f7370e1b --- /dev/null +++ b/src/config/lua/layout/LuaLayoutContext.cpp @@ -0,0 +1,144 @@ +#include "LuaLayoutContext.hpp" + +#include "LuaLayoutTarget.hpp" +#include "../bindings/LuaBindingsInternal.hpp" + +#include <algorithm> +#include <cmath> +#include <string_view> + +using namespace Config::Lua::Layouts; + +void Config::Lua::Layouts::pushBox(lua_State* L, const CBox& box) { + lua_newtable(L); + lua_pushnumber(L, box.x); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, box.y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, box.w); + lua_setfield(L, -2, "w"); + lua_pushnumber(L, box.h); + lua_setfield(L, -2, "h"); +} + +bool Config::Lua::Layouts::boxFromTable(lua_State* L, int idx, CBox& box) { + idx = lua_absindex(L, idx); + if (!lua_istable(L, idx)) + return false; + + auto getNum = [&](const char* key, double& out) -> bool { + lua_getfield(L, idx, key); + if (!lua_isnumber(L, -1)) { + lua_pop(L, 1); + return false; + } + out = lua_tonumber(L, -1); + lua_pop(L, 1); + return true; + }; + + return getNum("x", box.x) && getNum("y", box.y) && getNum("w", box.w) && getNum("h", box.h); +} + +static CBox areaFromContext(lua_State* L, int idx) { + idx = lua_absindex(L, idx); + CBox area; + lua_getfield(L, idx, "area"); + boxFromTable(L, -1, area); + lua_pop(L, 1); + return area; +} + +static size_t targetCountFromContext(lua_State* L, int idx) { + idx = lua_absindex(L, idx); + size_t count = 0; + lua_getfield(L, idx, "targets"); + if (lua_istable(L, -1)) + count = lua_rawlen(L, -1); + lua_pop(L, 1); + return count; +} + +static int ctxGridCell(lua_State* L) { + const auto AREA = areaFromContext(L, 1); + const int i = std::max(1, sc<int>(luaL_checkinteger(L, 2))); + const int cols = std::max(1, sc<int>(luaL_checkinteger(L, 3))); + int rows = 0; + + if (lua_gettop(L) >= 4 && lua_isnumber(L, 4)) + rows = std::max(1, sc<int>(lua_tointeger(L, 4))); + else { + const auto count = std::max<size_t>(1, targetCountFromContext(L, 1)); + rows = std::max(1, sc<int>(std::ceil(sc<double>(count) / sc<double>(cols)))); + } + + const int row = (i - 1) / cols; + const int col = (i - 1) % cols; + + pushBox(L, CBox{AREA.x + AREA.w * col / cols, AREA.y + AREA.h * row / rows, AREA.w / cols, AREA.h / rows}.noNegativeSize()); + return 1; +} + +static int ctxColumn(lua_State* L) { + const auto AREA = areaFromContext(L, 1); + const int i = std::max(1, sc<int>(luaL_checkinteger(L, 2))); + const int n = std::max(1, sc<int>(luaL_checkinteger(L, 3))); + + pushBox(L, CBox{AREA.x + AREA.w * (i - 1) / n, AREA.y, AREA.w / n, AREA.h}.noNegativeSize()); + return 1; +} + +static int ctxRow(lua_State* L) { + const auto AREA = areaFromContext(L, 1); + const int i = std::max(1, sc<int>(luaL_checkinteger(L, 2))); + const int n = std::max(1, sc<int>(luaL_checkinteger(L, 3))); + + pushBox(L, CBox{AREA.x, AREA.y + AREA.h * (i - 1) / n, AREA.w, AREA.h / n}.noNegativeSize()); + return 1; +} + +static int ctxSplit(lua_State* L) { + CBox area; + if (!boxFromTable(L, 2, area)) + return Config::Lua::Bindings::Internal::configError(L, "ctx:split expects a box table as first argument"); + + const std::string_view side = luaL_checkstring(L, 3); + const double ratio = std::clamp(luaL_checknumber(L, 4), 0.0, 1.0); + + if (side == "left") + pushBox(L, CBox{area.x, area.y, area.w * ratio, area.h}.noNegativeSize()); + else if (side == "right") + pushBox(L, CBox{area.x + area.w * (1.0 - ratio), area.y, area.w * ratio, area.h}.noNegativeSize()); + else if (side == "top" || side == "up") + pushBox(L, CBox{area.x, area.y, area.w, area.h * ratio}.noNegativeSize()); + else if (side == "bottom" || side == "down") + pushBox(L, CBox{area.x, area.y + area.h * (1.0 - ratio), area.w, area.h * ratio}.noNegativeSize()); + else + return Config::Lua::Bindings::Internal::configError(L, "ctx:split side must be left, right, top, or bottom"); + + return 1; +} + +void Config::Lua::Layouts::pushLayoutContext(lua_State* L, const std::vector<SP<Layout::ITarget>>& targets, const CBox& area) { + lua_newtable(L); + + pushBox(L, area); + lua_setfield(L, -2, "area"); + + lua_newtable(L); + int i = 1; + for (const auto& target : targets) { + pushLayoutTarget(L, target, i); + lua_rawseti(L, -2, i++); + } + lua_setfield(L, -2, "targets"); + + lua_pushcfunction(L, ctxGridCell); + lua_setfield(L, -2, "grid_cell"); + lua_pushcfunction(L, ctxColumn); + lua_setfield(L, -2, "column"); + lua_pushcfunction(L, ctxRow); + lua_setfield(L, -2, "row"); + lua_pushcfunction(L, ctxSplit); + lua_setfield(L, -2, "split"); +} |
