mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 04:38:10 +02:00
animations: add springs (#14171)
This commit is contained in:
parent
45ffaee093
commit
9ee5ff1f71
7 changed files with 142 additions and 60 deletions
|
|
@ -131,7 +131,7 @@ find_package(glslang CONFIG REQUIRED)
|
|||
set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
|
||||
set(HYPRLANG_MINIMUM_VERSION 0.6.7)
|
||||
set(HYPRCURSOR_MINIMUM_VERSION 0.1.7)
|
||||
set(HYPRUTILS_MINIMUM_VERSION 0.11.1)
|
||||
set(HYPRUTILS_MINIMUM_VERSION 0.13.0)
|
||||
set(HYPRGRAPHICS_MINIMUM_VERSION 0.5.1)
|
||||
|
||||
pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=${AQUAMARINE_MINIMUM_VERSION})
|
||||
|
|
|
|||
|
|
@ -139,10 +139,13 @@ hl.curve("linear", { type = "bezier", points = { {0, 0}, {1, 1}
|
|||
hl.curve("almostLinear", { type = "bezier", points = { {0.5, 0.5}, {0.75, 1} } })
|
||||
hl.curve("quick", { type = "bezier", points = { {0.15, 0}, {0.1, 1} } })
|
||||
|
||||
-- Default springs
|
||||
hl.curve("easy", { type = "spring", mass = 1, stiffness = 71.2633, dampening = 15.8273644 })
|
||||
|
||||
hl.animation({ leaf = "global", enabled = true, speed = 10, bezier = "default" })
|
||||
hl.animation({ leaf = "border", enabled = true, speed = 5.39, bezier = "easeOutQuint" })
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, bezier = "easeOutQuint" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, bezier = "easeOutQuint", style = "popin 87%" })
|
||||
hl.animation({ leaf = "windows", enabled = true, speed = 4.79, spring = "easy" })
|
||||
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4.1, spring = "easy", style = "popin 87%" })
|
||||
hl.animation({ leaf = "windowsOut", enabled = true, speed = 1.49, bezier = "linear", style = "popin 87%" })
|
||||
hl.animation({ leaf = "fadeIn", enabled = true, speed = 1.73, bezier = "almostLinear" })
|
||||
hl.animation({ leaf = "fadeOut", enabled = true, speed = 1.46, bezier = "almostLinear" })
|
||||
|
|
|
|||
6
flake.lock
generated
6
flake.lock
generated
|
|
@ -261,11 +261,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1777148223,
|
||||
"narHash": "sha256-PTf7kRFFzCW6rIYxLH2fWfVJmj86FSYe3k6L8B+IM9o=",
|
||||
"lastModified": 1777492286,
|
||||
"narHash": "sha256-PwuoEJQcjSKJNP5T55qhfDwIP0tw5zxEhfu8GDfKfeg=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "fa3992be2dfebe4ab06d753c6ca59bea298e798f",
|
||||
"rev": "ec5c0c709706bad5b82f667fd8758eae442577ce",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -40,9 +40,12 @@
|
|||
#include "../../../managers/input/trackpad/gestures/WorkspaceSwipeGesture.hpp"
|
||||
#include "../../../managers/permissions/DynamicPermissionManager.hpp"
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
||||
using namespace Config;
|
||||
using namespace Config::Lua;
|
||||
using namespace Config::Lua::Bindings;
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
namespace {
|
||||
struct SFieldDesc {
|
||||
|
|
@ -286,53 +289,101 @@ static int hlCurve(lua_State* L) {
|
|||
|
||||
const auto& curveType = typeParser.parsed();
|
||||
|
||||
if (curveType != "bezier")
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): unknown curve type \"{}\", expected \"bezier\"", name, curveType));
|
||||
|
||||
lua_getfield(L, 2, "points");
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): missing or invalid \"points\" field, expected a table of two points", name));
|
||||
}
|
||||
int pointsIdx = lua_gettop(L);
|
||||
|
||||
if (luaL_len(L, pointsIdx) != 2) {
|
||||
lua_pop(L, 1);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): \"points\" must contain exactly 2 points, e.g. {{ {{0, 0}}, {{1, 1}} }}", name));
|
||||
}
|
||||
|
||||
float coords[4] = {};
|
||||
for (int pt = 1; pt <= 2; pt++) {
|
||||
lua_rawgeti(L, pointsIdx, pt);
|
||||
if (!lua_istable(L, -1) || luaL_len(L, -1) != 2) {
|
||||
lua_pop(L, 2);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): point {} must be a table of 2 numbers, e.g. {{0.25, 0.1}}", name, pt));
|
||||
}
|
||||
int ptIdx = lua_gettop(L);
|
||||
|
||||
for (int comp = 0; comp < 2; comp++) {
|
||||
lua_rawgeti(L, ptIdx, comp + 1);
|
||||
CLuaConfigFloat coordParser(0.F, -1.F, 2.F);
|
||||
auto coordErr = coordParser.parse(L);
|
||||
if (curveType == "bezier") {
|
||||
lua_getfield(L, 2, "points");
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
if (coordErr.errorCode != PARSE_ERROR_OK) {
|
||||
lua_pop(L, 2);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): point {}[{}]: {}", name, pt, comp + 1, coordErr.message));
|
||||
}
|
||||
coords[((pt - 1) * 2) + comp] = coordParser.parsed();
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): missing or invalid \"points\" field, expected a table of two points", name));
|
||||
}
|
||||
int pointsIdx = lua_gettop(L);
|
||||
|
||||
if (luaL_len(L, pointsIdx) != 2) {
|
||||
lua_pop(L, 1);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): \"points\" must contain exactly 2 points, e.g. {{ {{0, 0}}, {{1, 1}} }}", name));
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
float coords[4] = {};
|
||||
for (int pt = 1; pt <= 2; pt++) {
|
||||
lua_rawgeti(L, pointsIdx, pt);
|
||||
if (!lua_istable(L, -1) || luaL_len(L, -1) != 2) {
|
||||
lua_pop(L, 2);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): point {} must be a table of 2 numbers, e.g. {{0.25, 0.1}}", name, pt));
|
||||
}
|
||||
int ptIdx = lua_gettop(L);
|
||||
|
||||
for (int comp = 0; comp < 2; comp++) {
|
||||
lua_rawgeti(L, ptIdx, comp + 1);
|
||||
CLuaConfigFloat coordParser(0.F, -1.F, 2.F);
|
||||
auto coordErr = coordParser.parse(L);
|
||||
lua_pop(L, 1);
|
||||
if (coordErr.errorCode != PARSE_ERROR_OK) {
|
||||
lua_pop(L, 2);
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): point {}[{}]: {}", name, pt, comp + 1, coordErr.message));
|
||||
}
|
||||
coords[((pt - 1) * 2) + comp] = coordParser.parsed();
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
g_pAnimationManager->addBezierWithName(name, Vector2D(coords[0], coords[1]), Vector2D(coords[2], coords[3]));
|
||||
} else if (curveType == "spring") {
|
||||
|
||||
Hyprutils::Animation::SSpringCurve curve;
|
||||
|
||||
{
|
||||
CScopeGuard x([L] { lua_pop(L, 1); });
|
||||
|
||||
lua_getfield(L, 2, "stiffness");
|
||||
|
||||
if (!lua_isnumber(L, -1))
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): stiffness expects a number", name));
|
||||
|
||||
curve.stiffness = lua_tonumber(L, -1);
|
||||
|
||||
if (curve.stiffness <= 0.5F)
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): stiffness expects a number >= 0.5", name));
|
||||
}
|
||||
|
||||
{
|
||||
CScopeGuard x([L] { lua_pop(L, 1); });
|
||||
|
||||
lua_getfield(L, 2, "dampening");
|
||||
|
||||
if (!lua_isnumber(L, -1))
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): dampening expects a number", name));
|
||||
|
||||
curve.damping = lua_tonumber(L, -1);
|
||||
|
||||
if (curve.damping <= 0.5F)
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): dampening expects a number >= 0.5", name));
|
||||
}
|
||||
|
||||
{
|
||||
CScopeGuard x([L] { lua_pop(L, 1); });
|
||||
|
||||
lua_getfield(L, 2, "mass");
|
||||
|
||||
if (!lua_isnumber(L, -1))
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): mass expects a number", name));
|
||||
|
||||
curve.mass = lua_tonumber(L, -1);
|
||||
|
||||
if (curve.mass <= 0.5F)
|
||||
return Internal::configError(L, std::format("hl.curve(\"{}\"): mass expects a number >= 0.5", name));
|
||||
}
|
||||
|
||||
g_pAnimationManager->addSpringWithName(name, curve);
|
||||
} else
|
||||
return Internal::configError(L, std::format(R"(hl.curve("{}"): unknown curve type "{}", expected "bezier" or "spring")", name, curveType));
|
||||
|
||||
g_pAnimationManager->addBezierWithName(name, Vector2D(coords[0], coords[1]), Vector2D(coords[2], coords[3]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hlAnimation(lua_State* L) {
|
||||
if (!lua_istable(L, 1))
|
||||
return Internal::configError(L, "hl.animation: expected a table, e.g. { leaf = \"global\", enabled = true, speed = 5, bezier = \"default\" }");
|
||||
return Internal::configError(L, R"(hl.animation: expected a table, e.g. { leaf = "global", enabled = true, speed = 5, bezier = "default" })");
|
||||
|
||||
CLuaConfigString leafParser("");
|
||||
auto leafErr = Internal::parseTableField(L, 1, "leaf", leafParser);
|
||||
|
|
@ -366,15 +417,34 @@ static int hlAnimation(lua_State* L) {
|
|||
if (speed <= 0)
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): speed must be greater than 0", leaf));
|
||||
|
||||
CLuaConfigString bezierParser("");
|
||||
auto bezierErr = Internal::parseTableField(L, 1, "bezier", bezierParser);
|
||||
if (bezierErr.errorCode != PARSE_ERROR_OK)
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): {}", leaf, bezierErr.message));
|
||||
std::string curveName;
|
||||
|
||||
const auto& bezierName = bezierParser.parsed();
|
||||
if (Internal::hasTableField(L, 1, "bezier")) {
|
||||
CLuaConfigString bezierParser("");
|
||||
auto bezierErr = Internal::parseTableField(L, 1, "bezier", bezierParser);
|
||||
if (bezierErr.errorCode != PARSE_ERROR_OK)
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): {}", leaf, bezierErr.message));
|
||||
|
||||
if (!g_pAnimationManager->bezierExists(bezierName))
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): no such bezier \"{}\"", leaf, bezierName));
|
||||
const auto& bezierName = bezierParser.parsed();
|
||||
|
||||
if (!g_pAnimationManager->bezierExists(bezierName))
|
||||
return Internal::configError(L, std::format(R"(hl.animation("{}"): no such bezier "{}")", leaf, bezierName));
|
||||
|
||||
curveName = bezierName;
|
||||
} else if (Internal::hasTableField(L, 1, "spring")) {
|
||||
CLuaConfigString springParser("");
|
||||
auto springErr = Internal::parseTableField(L, 1, "spring", springParser);
|
||||
if (springErr.errorCode != PARSE_ERROR_OK)
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): {}", leaf, springErr.message));
|
||||
|
||||
const auto& springName = springParser.parsed();
|
||||
|
||||
if (!g_pAnimationManager->springExists(springName))
|
||||
return Internal::configError(L, std::format(R"(hl.animation("{}"): no such spring "{}")", leaf, springName));
|
||||
|
||||
curveName = "spring:" + springName;
|
||||
} else
|
||||
return Internal::configError(L, std::format(R"(hl.animation("{}"): bezier or spring is required)", leaf));
|
||||
|
||||
std::string style;
|
||||
lua_getfield(L, 1, "style");
|
||||
|
|
@ -383,7 +453,7 @@ static int hlAnimation(lua_State* L) {
|
|||
auto styleErr = styleParser.parse(L);
|
||||
if (styleErr.errorCode != PARSE_ERROR_OK) {
|
||||
lua_pop(L, 1);
|
||||
return Internal::configError(L, std::format("hl.animation(\"{}\"): field \"style\": {}", leaf, styleErr.message));
|
||||
return Internal::configError(L, std::format(R"(hl.animation("{}"): field "style": {})", leaf, styleErr.message));
|
||||
}
|
||||
style = styleParser.parsed();
|
||||
}
|
||||
|
|
@ -395,7 +465,7 @@ static int hlAnimation(lua_State* L) {
|
|||
return Internal::configError(L, std::format("hl.animation(\"{}\"): {}", leaf, err));
|
||||
}
|
||||
|
||||
Config::animationTree()->setConfigForNode(leaf, true, speed, bezierName, style);
|
||||
Config::animationTree()->setConfigForNode(leaf, true, speed, curveName, style);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -574,3 +574,14 @@ std::expected<SP<Desktop::Rule::CWindowRule>, int> Internal::buildRuleFromTable(
|
|||
|
||||
return rule;
|
||||
}
|
||||
|
||||
bool Internal::hasTableField(lua_State* L, int tableIdx, const char* field) {
|
||||
lua_getfield(L, tableIdx, field);
|
||||
if (lua_isnoneornil(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ namespace Config::Lua::Bindings::Internal {
|
|||
return err;
|
||||
}
|
||||
|
||||
bool hasTableField(lua_State* L, int tableIdx, const char* field);
|
||||
void registerToplevelBindings(lua_State* L, CConfigManager* mgr);
|
||||
void registerQueryBindings(lua_State* L);
|
||||
void registerNotificationBindings(lua_State* L);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ static void updateVariable(CAnimatedVariable<VarType>& av, const float POINTY, b
|
|||
av.value() = av.begun() + DELTA * POINTY;
|
||||
}
|
||||
|
||||
static void updateColorVariable(CAnimatedVariable<CHyprColor>& av, const float POINTY, bool warp) {
|
||||
static void updateColorVariable(CAnimatedVariable<CHyprColor>& av, const float POINTY, bool warp = false) {
|
||||
if (warp || av.value() == av.goal()) {
|
||||
av.warp(true, false);
|
||||
return;
|
||||
|
|
@ -139,15 +139,12 @@ static void handleUpdate(CAnimatedVariable<VarType>& av, bool warp) {
|
|||
animationsDisabled = animationsDisabled || ls->m_ruleApplicator->noanim().valueOrDefault();
|
||||
}
|
||||
|
||||
const auto SPENT = av.getPercent();
|
||||
const auto PBEZIER = g_pAnimationManager->getBezier(av.getBezierName());
|
||||
const auto POINTY = PBEZIER->getYForPoint(SPENT);
|
||||
const bool WARP = animationsDisabled || SPENT >= 1.f;
|
||||
const auto STEP = av.getCurveStep();
|
||||
|
||||
if constexpr (std::same_as<VarType, CHyprColor>)
|
||||
updateColorVariable(av, POINTY, WARP);
|
||||
updateColorVariable(av, STEP.value, STEP.finished);
|
||||
else
|
||||
updateVariable<VarType>(av, POINTY, WARP);
|
||||
updateVariable<VarType>(av, STEP.value, STEP.finished);
|
||||
|
||||
av.onUpdate();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue