From 9be6fbf5eae8c4b902b818881a4dc34f87bb630d Mon Sep 17 00:00:00 2001
From: Vaxry <43317083+vaxerski@users.noreply.github.com>
Date: Sat, 11 Nov 2023 14:37:17 +0000
Subject: [PATCH] decorations: Decoration Positioner (#3800)

---
 src/Compositor.cpp                            |   7 +-
 src/SharedDefs.hpp                            |   4 +-
 src/Window.cpp                                |  92 +++---
 src/Window.hpp                                |   2 +
 src/helpers/Box.cpp                           |  13 +
 src/helpers/Box.hpp                           |  35 ++-
 src/helpers/Region.cpp                        |   9 +-
 src/helpers/Region.hpp                        |   3 +-
 src/helpers/Vector2D.cpp                      |   4 +
 src/helpers/Vector2D.hpp                      |   1 +
 src/layout/DwindleLayout.cpp                  |   8 +-
 src/layout/MasterLayout.cpp                   |   8 +-
 src/managers/AnimationManager.cpp             |  23 +-
 src/managers/KeybindManager.cpp               |   8 +-
 src/managers/input/InputManager.cpp           |   6 +-
 src/plugins/PluginAPI.cpp                     |   9 +-
 src/plugins/PluginAPI.hpp                     |   2 +-
 src/render/OpenGL.cpp                         |   4 +-
 src/render/Renderer.cpp                       |   2 +-
 .../decorations/CHyprDropShadowDecoration.cpp |  66 ++---
 .../decorations/CHyprDropShadowDecoration.hpp |  18 +-
 .../decorations/CHyprGroupBarDecoration.cpp   | 105 ++++---
 .../decorations/CHyprGroupBarDecoration.hpp   |  29 +-
 .../decorations/DecorationPositioner.cpp      | 277 ++++++++++++++++++
 .../decorations/DecorationPositioner.hpp      |  96 ++++++
 .../decorations/IHyprWindowDecoration.cpp     |  17 --
 .../decorations/IHyprWindowDecoration.hpp     |  28 +-
 27 files changed, 610 insertions(+), 266 deletions(-)
 create mode 100644 src/render/decorations/DecorationPositioner.cpp
 create mode 100644 src/render/decorations/DecorationPositioner.hpp

diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 30e9fdb6a..29d019c10 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -81,6 +81,7 @@ CCompositor::CCompositor() {
 
 CCompositor::~CCompositor() {
     cleanup();
+    g_pDecorationPositioner.reset();
     g_pPluginSystem.reset();
     g_pHyprNotificationOverlay.reset();
     g_pDebugOverlay.reset();
@@ -441,6 +442,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
             Debug::log(LOG, "Creating the PluginSystem!");
             g_pPluginSystem = std::make_unique<CPluginSystem>();
             g_pConfigManager->handlePluginLoads();
+
+            Debug::log(LOG, "Creating the DecorationPositioner!");
+            g_pDecorationPositioner = std::make_unique<CDecorationPositioner>();
         } break;
         default: UNREACHABLE();
     }
@@ -1920,8 +1924,7 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) {
         pWindow->m_cRealShadowColor.setValueAndWarp(CColor(0, 0, 0, 0)); // no shadow
     }
 
-    for (auto& d : pWindow->m_dWindowDecorations)
-        d->updateWindow(pWindow);
+    pWindow->updateWindowDecos();
 }
 
 int CCompositor::getNextAvailableMonitorID(std::string const& name) {
diff --git a/src/SharedDefs.hpp b/src/SharedDefs.hpp
index fb99caaa7..4e4217d2b 100644
--- a/src/SharedDefs.hpp
+++ b/src/SharedDefs.hpp
@@ -39,7 +39,7 @@ struct SWindowDecorationExtents {
         return SWindowDecorationExtents{topLeft * scale, bottomRight * scale};
     }
 
-    SWindowDecorationExtents floor() {
-        return {topLeft.floor(), bottomRight.floor()};
+    SWindowDecorationExtents round() {
+        return {topLeft.round(), bottomRight.round()};
     }
 };
\ No newline at end of file
diff --git a/src/Window.cpp b/src/Window.cpp
index 7994b6cbc..c1b79c2d2 100644
--- a/src/Window.cpp
+++ b/src/Window.cpp
@@ -13,7 +13,7 @@ CWindow::CWindow() {
     m_cRealShadowColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), (void*)this, AVARDAMAGE_SHADOW);
     m_fDimPercent.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeDim"), (void*)this, AVARDAMAGE_ENTIRE);
 
-    m_dWindowDecorations.emplace_back(std::make_unique<CHyprDropShadowDecoration>(this)); // put the shadow so it's the first deco (has to be rendered first)
+    addWindowDeco(std::make_unique<CHyprDropShadowDecoration>(this));
 }
 
 CWindow::~CWindow() {
@@ -37,22 +37,19 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() {
 
     SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
 
-    for (auto& wd : m_dWindowDecorations) {
+    const auto               EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this);
 
-        const auto EXTENTS = wd->getWindowDecorationExtents();
+    if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
+        maxExtents.topLeft.x = EXTENTS.topLeft.x;
 
-        if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
-            maxExtents.topLeft.x = EXTENTS.topLeft.x;
+    if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
+        maxExtents.topLeft.y = EXTENTS.topLeft.y;
 
-        if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
-            maxExtents.topLeft.y = EXTENTS.topLeft.y;
+    if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
+        maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
 
-        if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
-            maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
-
-        if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
-            maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
-    }
+    if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
+        maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
 
     if (m_pWLSurface.exists() && !m_bIsX11) {
         CBox surfaceExtents = {0, 0, 0, 0};
@@ -144,25 +141,19 @@ CBox CWindow::getWindowInputBox() {
 
     SWindowDecorationExtents maxExtents = {{BORDERSIZE + 2, BORDERSIZE + 2}, {BORDERSIZE + 2, BORDERSIZE + 2}};
 
-    for (auto& wd : m_dWindowDecorations) {
+    const auto               EXTENTS = g_pDecorationPositioner->getWindowDecorationExtents(this, true);
 
-        if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
-            continue;
+    if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
+        maxExtents.topLeft.x = EXTENTS.topLeft.x;
 
-        const auto EXTENTS = wd->getWindowDecorationExtents();
+    if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
+        maxExtents.topLeft.y = EXTENTS.topLeft.y;
 
-        if (EXTENTS.topLeft.x > maxExtents.topLeft.x)
-            maxExtents.topLeft.x = EXTENTS.topLeft.x;
+    if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
+        maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
 
-        if (EXTENTS.topLeft.y > maxExtents.topLeft.y)
-            maxExtents.topLeft.y = EXTENTS.topLeft.y;
-
-        if (EXTENTS.bottomRight.x > maxExtents.bottomRight.x)
-            maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
-
-        if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
-            maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
-    }
+    if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y)
+        maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
 
     // Add extents to the real base BB and return
     CBox finalBox = {m_vRealPosition.vec().x - maxExtents.topLeft.x, m_vRealPosition.vec().y - maxExtents.topLeft.y,
@@ -176,30 +167,19 @@ CBox CWindow::getWindowMainSurfaceBox() {
 }
 
 SWindowDecorationExtents CWindow::getFullWindowReservedArea() {
-    SWindowDecorationExtents extents;
-
-    for (auto& wd : m_dWindowDecorations) {
-        const auto RESERVED = wd->getWindowDecorationReservedArea();
-
-        if (RESERVED.bottomRight == Vector2D{} && RESERVED.topLeft == Vector2D{})
-            continue;
-
-        extents.topLeft     = extents.topLeft + RESERVED.topLeft;
-        extents.bottomRight = extents.bottomRight + RESERVED.bottomRight;
-    }
-
-    return extents;
+    return g_pDecorationPositioner->getWindowDecorationReserved(this);
 }
 
 void CWindow::updateWindowDecos() {
-    for (auto& wd : m_dWindowDecorations)
-        wd->updateWindow(this);
-
     bool recalc = false;
 
+    if (!m_bIsMapped || isHidden())
+        return;
+
     for (auto& wd : m_vDecosToRemove) {
         for (auto it = m_dWindowDecorations.begin(); it != m_dWindowDecorations.end(); it++) {
             if (it->get() == wd) {
+                g_pDecorationPositioner->uncacheDecoration(it->get());
                 it     = m_dWindowDecorations.erase(it);
                 recalc = true;
                 if (it == m_dWindowDecorations.end())
@@ -208,10 +188,26 @@ void CWindow::updateWindowDecos() {
         }
     }
 
+    g_pDecorationPositioner->onWindowUpdate(this);
+
     if (recalc)
         g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
 
     m_vDecosToRemove.clear();
+
+    for (auto& wd : m_dWindowDecorations) {
+        wd->updateWindow(this);
+    }
+}
+
+void CWindow::addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco) {
+    m_dWindowDecorations.emplace_back(std::move(deco));
+    updateWindowDecos();
+}
+
+void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
+    m_vDecosToRemove.push_back(deco);
+    updateWindowDecos();
 }
 
 pid_t CWindow::getPID() {
@@ -689,14 +685,14 @@ void CWindow::createGroup() {
         Debug::log(LOG, "createGroup: window:{:x},title:{} is denied as a group, ignored", (uintptr_t)this, this->m_szTitle);
         return;
     }
+
     if (!m_sGroupData.pNextWindow) {
         m_sGroupData.pNextWindow = this;
         m_sGroupData.head        = true;
         m_sGroupData.locked      = false;
         m_sGroupData.deny        = false;
 
-        m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(this));
-        updateWindowDecos();
+        addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(this));
 
         g_pLayoutManager->getCurrentLayout()->recalculateWindow(this);
         g_pCompositor->updateAllWindowsAnimatedDecorationValues();
@@ -835,6 +831,8 @@ void CWindow::setGroupCurrent(CWindow* pWindow) {
         g_pCompositor->setWindowFullscreen(pWindow, true, WORKSPACE->m_efFullscreenMode);
 
     g_pHyprRenderer->damageWindow(pWindow);
+
+    pWindow->updateWindowDecos();
 }
 
 void CWindow::insertWindowToGroup(CWindow* pWindow) {
@@ -842,7 +840,7 @@ void CWindow::insertWindowToGroup(CWindow* pWindow) {
     const auto ENDAT   = m_sGroupData.pNextWindow;
 
     if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
-        pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
+        pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
 
     if (!pWindow->m_sGroupData.pNextWindow) {
         BEGINAT->m_sGroupData.pNextWindow = pWindow;
diff --git a/src/Window.hpp b/src/Window.hpp
index 580b0a2a6..4e1ab9c14 100644
--- a/src/Window.hpp
+++ b/src/Window.hpp
@@ -344,7 +344,9 @@ class CWindow {
     CBox                     getWindowInputBox();
     CBox                     getWindowMainSurfaceBox();
     CBox                     getWindowIdealBoundingBoxIgnoreReserved();
+    void                     addWindowDeco(std::unique_ptr<IHyprWindowDecoration> deco);
     void                     updateWindowDecos();
+    void                     removeWindowDeco(IHyprWindowDecoration* deco);
     pid_t                    getPID();
     IHyprWindowDecoration*   getDecorationByType(eDecorationType);
     void                     removeDecorationByType(eDecorationType);
diff --git a/src/helpers/Box.cpp b/src/helpers/Box.cpp
index 55300f5f9..29aa81fa8 100644
--- a/src/helpers/Box.cpp
+++ b/src/helpers/Box.cpp
@@ -96,6 +96,15 @@ CBox& CBox::scaleFromCenter(double scale) {
     return *this;
 }
 
+CBox& CBox::expand(const double& value) {
+    x -= value;
+    y -= value;
+    w += value * 2.0;
+    h += value * 2.0;
+
+    return *this;
+}
+
 CBox CBox::roundInternal() {
     float newW = x + w - std::floor(x);
     float newH = y + h - std::floor(y);
@@ -110,3 +119,7 @@ Vector2D CBox::pos() const {
 Vector2D CBox::size() const {
     return {w, h};
 }
+
+SWindowDecorationExtents CBox::extentsFrom(const CBox& small) {
+    return {{small.x - x, small.y - y}, {w - small.w - (small.x - x), h - small.h - (small.y - y)}};
+}
diff --git a/src/helpers/Box.hpp b/src/helpers/Box.hpp
index 592bb417e..cb07df068 100644
--- a/src/helpers/Box.hpp
+++ b/src/helpers/Box.hpp
@@ -39,26 +39,29 @@ class CBox {
         h = size.y;
     }
 
-    wlr_box  wlr();
-    wlr_box* pWlr();
+    wlr_box                  wlr();
+    wlr_box*                 pWlr();
 
-    CBox&    applyFromWlr();
-    CBox&    scale(double scale);
-    CBox&    scaleFromCenter(double scale);
-    CBox&    scale(const Vector2D& scale);
-    CBox&    translate(const Vector2D& vec);
-    CBox&    round();
-    CBox&    transform(const wl_output_transform t, double w, double h);
-    CBox&    addExtents(const SWindowDecorationExtents& e);
+    CBox&                    applyFromWlr();
+    CBox&                    scale(double scale);
+    CBox&                    scaleFromCenter(double scale);
+    CBox&                    scale(const Vector2D& scale);
+    CBox&                    translate(const Vector2D& vec);
+    CBox&                    round();
+    CBox&                    transform(const wl_output_transform t, double w, double h);
+    CBox&                    addExtents(const SWindowDecorationExtents& e);
+    CBox&                    expand(const double& value);
 
-    Vector2D middle() const;
-    Vector2D pos() const;
-    Vector2D size() const;
+    SWindowDecorationExtents extentsFrom(const CBox&); // this is the big box
 
-    bool     containsPoint(const Vector2D& vec) const;
-    bool     empty() const;
+    Vector2D                 middle() const;
+    Vector2D                 pos() const;
+    Vector2D                 size() const;
 
-    double   x = 0, y = 0;
+    bool                     containsPoint(const Vector2D& vec) const;
+    bool                     empty() const;
+
+    double                   x = 0, y = 0;
     union {
         double w;
         double width;
diff --git a/src/helpers/Region.cpp b/src/helpers/Region.cpp
index fdf8a2427..06b8536ce 100644
--- a/src/helpers/Region.cpp
+++ b/src/helpers/Region.cpp
@@ -21,8 +21,8 @@ CRegion::CRegion(wlr_box* box) {
     pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->width, box->height);
 }
 
-CRegion::CRegion(CBox* box) {
-    pixman_region32_init_rect(&m_rRegion, box->x, box->y, box->w, box->h);
+CRegion::CRegion(const CBox& box) {
+    pixman_region32_init_rect(&m_rRegion, box.x, box.y, box.w, box.h);
 }
 
 CRegion::CRegion(pixman_box32_t* box) {
@@ -63,6 +63,11 @@ CRegion& CRegion::add(double x, double y, double w, double h) {
     return *this;
 }
 
+CRegion& CRegion::add(const CBox& other) {
+    pixman_region32_union_rect(&m_rRegion, &m_rRegion, other.x, other.y, other.w, other.h);
+    return *this;
+}
+
 CRegion& CRegion::subtract(const CRegion& other) {
     pixman_region32_subtract(&m_rRegion, &m_rRegion, const_cast<CRegion*>(&other)->pixman());
     return *this;
diff --git a/src/helpers/Region.hpp b/src/helpers/Region.hpp
index 80153cff1..1c6923dff 100644
--- a/src/helpers/Region.hpp
+++ b/src/helpers/Region.hpp
@@ -17,7 +17,7 @@ class CRegion {
     /* Create from a wlr_box */
     CRegion(wlr_box* box);
     /* Create from a CBox */
-    CRegion(CBox* box);
+    CRegion(const CBox& box);
     /* Create from a pixman_box32_t */
     CRegion(pixman_box32_t* box);
 
@@ -40,6 +40,7 @@ class CRegion {
     CRegion&                    set(const CRegion& other);
     CRegion&                    add(const CRegion& other);
     CRegion&                    add(double x, double y, double w, double h);
+    CRegion&                    add(const CBox& other);
     CRegion&                    subtract(const CRegion& other);
     CRegion&                    intersect(const CRegion& other);
     CRegion&                    intersect(double x, double y, double w, double h);
diff --git a/src/helpers/Vector2D.cpp b/src/helpers/Vector2D.cpp
index ae2484b3c..7bc6412da 100644
--- a/src/helpers/Vector2D.cpp
+++ b/src/helpers/Vector2D.cpp
@@ -28,6 +28,10 @@ Vector2D Vector2D::floor() const {
     return Vector2D(std::floor(x), std::floor(y));
 }
 
+Vector2D Vector2D::round() const {
+    return Vector2D(std::round(x), std::round(y));
+}
+
 Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) const {
     return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
 }
diff --git a/src/helpers/Vector2D.hpp b/src/helpers/Vector2D.hpp
index 17415289d..c79c967ce 100644
--- a/src/helpers/Vector2D.hpp
+++ b/src/helpers/Vector2D.hpp
@@ -61,6 +61,7 @@ class Vector2D {
     Vector2D clamp(const Vector2D& min, const Vector2D& max = Vector2D()) const;
 
     Vector2D floor() const;
+    Vector2D round() const;
 
     bool     inTriangle(const Vector2D& p1, const Vector2D& p2, const Vector2D& p3) const;
 };
diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp
index 6602c7586..a01c3b8c4 100644
--- a/src/layout/DwindleLayout.cpp
+++ b/src/layout/DwindleLayout.cpp
@@ -323,7 +323,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire
             if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
                 continue;
 
-            if (wd->getWindowDecorationRegion().containsPoint(MOUSECOORDS)) {
+            if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(MOUSECOORDS)) {
                 if (!wd->onEndWindowDragOnDeco(pWindow, MOUSECOORDS))
                     return;
                 break;
@@ -334,9 +334,6 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire
     // if it's a group, add the window
     if (OPENINGON->pWindow->m_sGroupData.pNextWindow                                  // target is group
         && pWindow->canBeGroupedInto(OPENINGON->pWindow) && !m_vOverrideFocalPoint) { // we are not moving window
-        if (!pWindow->m_sGroupData.pNextWindow)
-            pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
-
         m_lDwindleNodesData.remove(*PNODE);
 
         static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue;
@@ -347,6 +344,9 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection dire
         pWindow->updateWindowDecos();
         recalculateWindow(pWindow);
 
+        if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
+            pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
+
         return;
     }
 
diff --git a/src/layout/MasterLayout.cpp b/src/layout/MasterLayout.cpp
index 577847866..d355a7d39 100644
--- a/src/layout/MasterLayout.cpp
+++ b/src/layout/MasterLayout.cpp
@@ -99,7 +99,7 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc
             if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
                 continue;
 
-            if (wd->getWindowDecorationRegion().containsPoint(MOUSECOORDS)) {
+            if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(MOUSECOORDS)) {
                 if (!wd->onEndWindowDragOnDeco(pWindow, MOUSECOORDS))
                     return;
                 break;
@@ -111,9 +111,6 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc
     if (OPENINGON && OPENINGON != PNODE && OPENINGON->pWindow->m_sGroupData.pNextWindow // target is group
         && pWindow->canBeGroupedInto(OPENINGON->pWindow)) {
 
-        if (!pWindow->m_sGroupData.pNextWindow)
-            pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
-
         m_lMasterNodesData.remove(*PNODE);
 
         static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue;
@@ -124,6 +121,9 @@ void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow, eDirection direc
         pWindow->updateWindowDecos();
         recalculateWindow(pWindow);
 
+        if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
+            pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
+
         return;
     }
 
diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp
index 1dc8e2c6d..a5e1c47e3 100644
--- a/src/managers/AnimationManager.cpp
+++ b/src/managers/AnimationManager.cpp
@@ -248,28 +248,9 @@ void CAnimationManager::tick() {
             case AVARDAMAGE_SHADOW: {
                 RASSERT(PWINDOW, "Tried to AVARDAMAGE_SHADOW a non-window AVAR!");
 
-                static auto* const PSHADOWIGNOREWINDOW = &g_pConfigManager->getConfigValuePtr("decoration:shadow_ignore_window")->intValue;
+                const auto PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW);
 
-                const auto         PDECO = PWINDOW->getDecorationByType(DECORATION_SHADOW);
-
-                if (PDECO) {
-                    const auto EXTENTS = PDECO->getWindowDecorationExtents();
-
-                    CBox       dmg = {PWINDOW->m_vRealPosition.vec().x - EXTENTS.topLeft.x, PWINDOW->m_vRealPosition.vec().y - EXTENTS.topLeft.y,
-                                PWINDOW->m_vRealSize.vec().x + EXTENTS.topLeft.x + EXTENTS.bottomRight.x, PWINDOW->m_vRealSize.vec().y + EXTENTS.topLeft.y + EXTENTS.bottomRight.y};
-
-                    if (!*PSHADOWIGNOREWINDOW) {
-                        // easy, damage the entire box
-                        g_pHyprRenderer->damageBox(&dmg);
-                    } else {
-                        CRegion rg{dmg.x, dmg.y, dmg.width, dmg.height};
-                        CRegion wb{PWINDOW->m_vRealPosition.vec().x, PWINDOW->m_vRealPosition.vec().y, PWINDOW->m_vRealSize.vec().x, PWINDOW->m_vRealSize.vec().y};
-
-                        rg.subtract(wb);
-
-                        g_pHyprRenderer->damageRegion(rg);
-                    }
-                }
+                PDECO->damageEntire();
 
                 break;
             }
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index 3bed027dd..e4cac5f47 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -1845,7 +1845,7 @@ void CKeybindManager::mouse(std::string args) {
                     if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
                         continue;
 
-                    if (wd->getWindowDecorationRegion().containsPoint(mouseCoords)) {
+                    if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) {
                         wd->onBeginWindowDragOnDeco(mouseCoords);
                         break;
                     }
@@ -1964,9 +1964,6 @@ void CKeybindManager::moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDi
     if (pWindow->m_sGroupData.deny)
         return;
 
-    if (!pWindow->m_sGroupData.pNextWindow)
-        pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
-
     g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property!
 
     static const auto* USECURRPOS = &g_pConfigManager->getConfigValuePtr("group:insert_after_current")->intValue;
@@ -1978,6 +1975,9 @@ void CKeybindManager::moveWindowIntoGroup(CWindow* pWindow, CWindow* pWindowInDi
     g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
     g_pCompositor->focusWindow(pWindow);
     g_pCompositor->warpCursorTo(pWindow->middle());
+
+    if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
+        pWindow->addWindowDeco(std::make_unique<CHyprGroupBarDecoration>(pWindow));
 }
 
 void CKeybindManager::moveWindowOutOfGroup(CWindow* pWindow, const std::string& dir) {
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index 78ed3d710..d096ab32d 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -588,7 +588,7 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
             if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
                 continue;
 
-            if (wd->getWindowDecorationRegion().containsPoint(mouseCoords)) {
+            if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) {
                 wd->onMouseButtonOnDeco(mouseCoords, e);
                 return;
             }
@@ -675,7 +675,7 @@ void CInputManager::onMouseWheel(wlr_pointer_axis_event* e) {
     const auto pWindow     = g_pCompositor->vectorToWindowIdeal(MOUSECOORDS);
 
     if (*PGROUPBARSCROLLING && pWindow && !pWindow->m_bIsFullscreen && !pWindow->hasPopupAt(MOUSECOORDS) && pWindow->m_sGroupData.pNextWindow) {
-        const CBox box = pWindow->getDecorationByType(DECORATION_GROUPBAR)->getWindowDecorationRegion().getExtents();
+        const CBox box = g_pDecorationPositioner->getWindowDecorationBox(pWindow->getDecorationByType(DECORATION_GROUPBAR));
         if (box.containsPoint(MOUSECOORDS)) {
             if (e->delta > 0)
                 pWindow->setGroupCurrent(pWindow->m_sGroupData.pNextWindow);
@@ -1638,7 +1638,7 @@ void CInputManager::setCursorIconOnBorder(CWindow* w) {
             if (!(wd->getDecorationFlags() & DECORATION_ALLOWS_MOUSE_INPUT))
                 continue;
 
-            if (wd->getWindowDecorationRegion().containsPoint(mouseCoords)) {
+            if (g_pDecorationPositioner->getWindowDecorationBox(wd.get()).containsPoint(mouseCoords)) {
                 onDeco = true;
                 break;
             }
diff --git a/src/plugins/PluginAPI.cpp b/src/plugins/PluginAPI.cpp
index 1395728d2..283472fae 100644
--- a/src/plugins/PluginAPI.cpp
+++ b/src/plugins/PluginAPI.cpp
@@ -111,7 +111,7 @@ APICALL bool HyprlandAPI::removeFunctionHook(HANDLE handle, CFunctionHook* hook)
     return g_pFunctionHookSystem->removeHook(hook);
 }
 
-APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration) {
+APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, CWindow* pWindow, std::unique_ptr<IHyprWindowDecoration> pDecoration) {
     auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
 
     if (!PLUGIN)
@@ -120,11 +120,10 @@ APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, CWindow* pWindow, I
     if (!g_pCompositor->windowValidMapped(pWindow))
         return false;
 
-    PLUGIN->registeredDecorations.push_back(pDecoration);
+    PLUGIN->registeredDecorations.push_back(pDecoration.get());
 
-    pWindow->m_dWindowDecorations.emplace_back(pDecoration);
+    pWindow->addWindowDeco(std::move(pDecoration));
 
-    pWindow->updateWindowDecos();
     g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
 
     return true;
@@ -139,7 +138,7 @@ APICALL bool HyprlandAPI::removeWindowDecoration(HANDLE handle, IHyprWindowDecor
     for (auto& w : g_pCompositor->m_vWindows) {
         for (auto& d : w->m_dWindowDecorations) {
             if (d.get() == pDecoration) {
-                std::erase(w->m_dWindowDecorations, d);
+                w->removeWindowDeco(pDecoration);
                 return true;
             }
         }
diff --git a/src/plugins/PluginAPI.hpp b/src/plugins/PluginAPI.hpp
index a3792fe9e..12e7c128b 100644
--- a/src/plugins/PluginAPI.hpp
+++ b/src/plugins/PluginAPI.hpp
@@ -221,7 +221,7 @@ namespace HyprlandAPI {
 
         returns: true on success. False otherwise.
     */
-    APICALL bool addWindowDecoration(HANDLE handle, CWindow* pWindow, IHyprWindowDecoration* pDecoration);
+    APICALL bool addWindowDecoration(HANDLE handle, CWindow* pWindow, std::unique_ptr<IHyprWindowDecoration> pDecoration);
 
     /*
         Removes a window decoration
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 26f3c1285..826854ed8 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -497,7 +497,7 @@ void CHyprOpenGLImpl::renderRectWithBlur(CBox* box, const CColor& col, int round
         return;
 
     CRegion damage{m_RenderData.damage};
-    damage.intersect(box);
+    damage.intersect(*box);
 
     CFramebuffer* POUTFB = blurMainFramebufferWithDamage(blurA, &damage);
 
@@ -1392,7 +1392,7 @@ void CHyprOpenGLImpl::renderBorder(CBox* box, const CGradientValueData& grad, in
     if (borderSize < 1)
         return;
 
-    int scaledBorderSize = borderSize * m_RenderData.pMonitor->scale * m_RenderData.renderModif.scale;
+    int scaledBorderSize = std::round(borderSize * m_RenderData.pMonitor->scale * m_RenderData.renderModif.scale);
 
     // adjust box
     box->x -= scaledBorderSize;
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index a43d6a2d8..98dcf7f5e 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -2105,7 +2105,7 @@ void CHyprRenderer::setOccludedForBackLayers(CRegion& region, CWorkspace* pWorks
 
         box.scale(PMONITOR->scale);
 
-        rg.add(&box);
+        rg.add(box);
     }
 
     region.subtract(rg);
diff --git a/src/render/decorations/CHyprDropShadowDecoration.cpp b/src/render/decorations/CHyprDropShadowDecoration.cpp
index ac6c5b43e..9f051ad3b 100644
--- a/src/render/decorations/CHyprDropShadowDecoration.cpp
+++ b/src/render/decorations/CHyprDropShadowDecoration.cpp
@@ -10,19 +10,18 @@ CHyprDropShadowDecoration::~CHyprDropShadowDecoration() {
     updateWindow(m_pWindow);
 }
 
-SWindowDecorationExtents CHyprDropShadowDecoration::getWindowDecorationExtents() {
-    static auto* const PSHADOWS = &g_pConfigManager->getConfigValuePtr("decoration:drop_shadow")->intValue;
-
-    if (*PSHADOWS != 1)
-        return {{}, {}};
-
-    return m_seExtents;
-}
-
 eDecorationType CHyprDropShadowDecoration::getDecorationType() {
     return DECORATION_SHADOW;
 }
 
+SDecorationPositioningInfo CHyprDropShadowDecoration::getPositioningInfo() {
+    return {DECORATION_POSITION_ABSOLUTE};
+}
+
+void CHyprDropShadowDecoration::onPositioningReply(const SDecorationPositioningReply& reply) {
+    updateWindow(m_pWindow);
+}
+
 void CHyprDropShadowDecoration::damageEntire() {
     static auto* const PSHADOWS = &g_pConfigManager->getConfigValuePtr("decoration:drop_shadow")->intValue;
 
@@ -40,37 +39,11 @@ void CHyprDropShadowDecoration::updateWindow(CWindow* pWindow) {
 
     const auto WORKSPACEOFFSET = PWORKSPACE && !pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.vec() : Vector2D();
 
-    if (pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET != m_vLastWindowPos || pWindow->m_vRealSize.vec() != m_vLastWindowSize) {
-        m_vLastWindowPos  = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET;
-        m_vLastWindowSize = pWindow->m_vRealSize.vec();
+    m_vLastWindowPos  = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET;
+    m_vLastWindowSize = pWindow->m_vRealSize.vec();
 
-        damageEntire();
-
-        const auto BORDER = m_pWindow->getRealBorderSize();
-
-        // calculate extents of decos with the DECORATION_PART_OF_MAIN_WINDOW flag
-        SWindowDecorationExtents maxExtents;
-
-        for (auto& wd : m_pWindow->m_dWindowDecorations) {
-            // conveniently, this will also skip us.
-            if (!(wd->getDecorationFlags() & DECORATION_PART_OF_MAIN_WINDOW))
-                continue;
-
-            const auto EXTENTS = wd->getWindowDecorationExtents();
-
-            if (maxExtents.topLeft.x < EXTENTS.topLeft.x)
-                maxExtents.topLeft.x = EXTENTS.topLeft.x;
-            if (maxExtents.topLeft.y < EXTENTS.topLeft.y)
-                maxExtents.topLeft.y = EXTENTS.topLeft.y;
-            if (maxExtents.bottomRight.x < EXTENTS.bottomRight.x)
-                maxExtents.bottomRight.x = EXTENTS.bottomRight.x;
-            if (maxExtents.bottomRight.y < EXTENTS.bottomRight.y)
-                maxExtents.bottomRight.y = EXTENTS.bottomRight.y;
-        }
-
-        m_bLastWindowBox = {m_vLastWindowPos.x, m_vLastWindowPos.y, m_vLastWindowSize.x, m_vLastWindowSize.y};
-        m_eLastExtents   = {{maxExtents.topLeft + Vector2D{BORDER, BORDER}}, {maxExtents.bottomRight + Vector2D{BORDER, BORDER}}};
-    }
+    m_bLastWindowBox          = {m_vLastWindowPos.x, m_vLastWindowPos.y, m_vLastWindowSize.x, m_vLastWindowSize.y};
+    m_bLastWindowBoxWithDecos = g_pDecorationPositioner->getBoxWithIncludedDecos(pWindow);
 }
 
 void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
@@ -102,8 +75,8 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D
     const auto ROUNDING = m_pWindow->rounding() + m_pWindow->getRealBorderSize();
 
     // draw the shadow
-    CBox fullBox = {m_bLastWindowBox.x, m_bLastWindowBox.y, m_bLastWindowBox.width, m_bLastWindowBox.height};
-    fullBox.addExtents(m_eLastExtents).translate(-pMonitor->vecPosition);
+    CBox fullBox = m_bLastWindowBoxWithDecos;
+    fullBox.translate(-pMonitor->vecPosition);
     fullBox.x -= *PSHADOWSIZE;
     fullBox.y -= *PSHADOWSIZE;
     fullBox.w += 2 * *PSHADOWSIZE;
@@ -134,11 +107,16 @@ void CHyprDropShadowDecoration::draw(CMonitor* pMonitor, float a, const Vector2D
 
     if (*PSHADOWIGNOREWINDOW) {
         CBox windowBox = m_bLastWindowBox;
+        CBox withDecos = m_bLastWindowBoxWithDecos;
 
-        windowBox.translate(-pMonitor->vecPosition).scale(pMonitor->scale);
-        windowBox.round();
+        // get window box
+        windowBox.translate(-pMonitor->vecPosition).scale(pMonitor->scale).round();
+        withDecos.translate(-pMonitor->vecPosition).scale(pMonitor->scale).round();
 
-        windowBox.addExtents(SWindowDecorationExtents{m_eLastExtents * pMonitor->scale}.floor()).round();
+        auto scaledDecoExtents = withDecos.extentsFrom(windowBox).round();
+
+        // add extents
+        windowBox.addExtents(scaledDecoExtents).round();
 
         if (windowBox.width < 1 || windowBox.height < 1)
             return; // prevent assert failed
diff --git a/src/render/decorations/CHyprDropShadowDecoration.hpp b/src/render/decorations/CHyprDropShadowDecoration.hpp
index d7ec043e6..9f0f58a03 100644
--- a/src/render/decorations/CHyprDropShadowDecoration.hpp
+++ b/src/render/decorations/CHyprDropShadowDecoration.hpp
@@ -7,17 +7,19 @@ class CHyprDropShadowDecoration : public IHyprWindowDecoration {
     CHyprDropShadowDecoration(CWindow*);
     virtual ~CHyprDropShadowDecoration();
 
-    virtual SWindowDecorationExtents getWindowDecorationExtents();
+    virtual SDecorationPositioningInfo getPositioningInfo();
 
-    virtual void                     draw(CMonitor*, float a, const Vector2D& offset);
+    virtual void                       onPositioningReply(const SDecorationPositioningReply& reply);
 
-    virtual eDecorationType          getDecorationType();
+    virtual void                       draw(CMonitor*, float a, const Vector2D& offset);
 
-    virtual void                     updateWindow(CWindow*);
+    virtual eDecorationType            getDecorationType();
 
-    virtual void                     damageEntire();
+    virtual void                       updateWindow(CWindow*);
 
-    virtual eDecorationLayer         getDecorationLayer();
+    virtual void                       damageEntire();
+
+    virtual eDecorationLayer           getDecorationLayer();
 
   private:
     SWindowDecorationExtents m_seExtents;
@@ -27,6 +29,6 @@ class CHyprDropShadowDecoration : public IHyprWindowDecoration {
     Vector2D                 m_vLastWindowPos;
     Vector2D                 m_vLastWindowSize;
 
-    CBox                     m_bLastWindowBox = {0};
-    SWindowDecorationExtents m_eLastExtents   = {};
+    CBox                     m_bLastWindowBox          = {0};
+    CBox                     m_bLastWindowBoxWithDecos = {0};
 };
\ No newline at end of file
diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp
index 873186c05..625888daa 100644
--- a/src/render/decorations/CHyprGroupBarDecoration.cpp
+++ b/src/render/decorations/CHyprGroupBarDecoration.cpp
@@ -6,6 +6,10 @@
 // shared things to conserve VRAM
 static CTexture m_tGradientActive;
 static CTexture m_tGradientInactive;
+constexpr int   BAR_INDICATOR_HEIGHT   = 3;
+constexpr int   BAR_PADDING_OUTER_VERT = 2;
+constexpr int   BAR_TEXT_PAD           = 2;
+constexpr int   BAR_HORIZONTAL_PADDING = 2;
 
 CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) : IHyprWindowDecoration(pWindow) {
     m_pWindow = pWindow;
@@ -13,47 +17,33 @@ CHyprGroupBarDecoration::CHyprGroupBarDecoration(CWindow* pWindow) : IHyprWindow
 
 CHyprGroupBarDecoration::~CHyprGroupBarDecoration() {}
 
-SWindowDecorationExtents CHyprGroupBarDecoration::getWindowDecorationExtents() {
-    return m_seExtents;
+SDecorationPositioningInfo CHyprGroupBarDecoration::getPositioningInfo() {
+    const int                  BORDERSIZE     = m_pWindow->getRealBorderSize();
+    static auto* const         PRENDERTITLES  = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
+    static auto* const         PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue;
+
+    SDecorationPositioningInfo info;
+    info.policy         = DECORATION_POSITION_STICKY;
+    info.edges          = DECORATION_EDGE_TOP;
+    info.priority       = 3;
+    info.reserved       = true;
+    info.desiredExtents = {{0, BORDERSIZE + BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2}, {0, 0}};
+    return info;
+}
+
+void CHyprGroupBarDecoration::onPositioningReply(const SDecorationPositioningReply& reply) {
+    m_bAssignedBox = reply.assignedGeometry;
 }
 
 eDecorationType CHyprGroupBarDecoration::getDecorationType() {
     return DECORATION_GROUPBAR;
 }
 
-constexpr int BAR_INDICATOR_HEIGHT   = 3;
-constexpr int BAR_PADDING_OUTER_VERT = 2;
-constexpr int BAR_TEXT_PAD           = 2;
-constexpr int BAR_HORIZONTAL_PADDING = 2;
-
 //
 
 void CHyprGroupBarDecoration::updateWindow(CWindow* pWindow) {
-    damageEntire();
-
-    const auto         PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
-
-    const auto         WORKSPACEOFFSET = PWORKSPACE && !pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.vec() : Vector2D();
-
-    static auto* const PRENDERTITLES  = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
-    static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue;
-
-    if (pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET != m_vLastWindowPos || pWindow->m_vRealSize.vec() != m_vLastWindowSize) {
-        // we draw 3px above the window's border with 3px
-
-        const int BORDERSIZE = pWindow->getRealBorderSize();
-
-        m_seExtents.topLeft     = Vector2D(0, BORDERSIZE + BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2);
-        m_seExtents.bottomRight = Vector2D();
-
-        m_vLastWindowPos  = pWindow->m_vRealPosition.vec() + WORKSPACEOFFSET;
-        m_vLastWindowSize = pWindow->m_vRealSize.vec();
-
-        invalidateTextures();
-    }
-
     if (!m_pWindow->m_sGroupData.pNextWindow) {
-        m_pWindow->m_vDecosToRemove.push_back(this);
+        m_pWindow->removeWindowDeco(this);
         return;
     }
 
@@ -70,15 +60,14 @@ void CHyprGroupBarDecoration::updateWindow(CWindow* pWindow) {
     damageEntire();
 
     if (m_dwGroupMembers.size() == 0) {
-        m_pWindow->m_vDecosToRemove.push_back(this);
+        m_pWindow->removeWindowDeco(this);
         return;
     }
 }
 
 void CHyprGroupBarDecoration::damageEntire() {
-    CBox dm = {m_vLastWindowPos.x - m_seExtents.topLeft.x, m_vLastWindowPos.y - m_seExtents.topLeft.y, m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x,
-               m_seExtents.topLeft.y};
-    g_pHyprRenderer->damageBox(&dm);
+    auto box = assignedBoxGlobal();
+    g_pHyprRenderer->damageBox(&box);
 }
 
 void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
@@ -94,13 +83,19 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
     if (!m_pWindow->m_sSpecialRenderData.decorate)
         return;
 
-    m_fBarWidth = (m_vLastWindowSize.x - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
+    const auto ASSIGNEDBOX = assignedBoxGlobal();
+
+    m_fBarWidth = (ASSIGNEDBOX.w - BAR_HORIZONTAL_PADDING * (barsToDraw - 1)) / barsToDraw;
+
+    const auto DESIREDHEIGHT = BORDERSIZE + BAR_PADDING_OUTER_VERT * 2 + BAR_INDICATOR_HEIGHT + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0) + 2;
+    if (DESIREDHEIGHT != ASSIGNEDBOX.h)
+        g_pDecorationPositioner->repositionDeco(this);
 
     int xoff = 0;
 
     for (int i = 0; i < barsToDraw; ++i) {
-        CBox rect = {m_vLastWindowPos.x + xoff - pMonitor->vecPosition.x + offset.x,
-                     m_vLastWindowPos.y - BAR_PADDING_OUTER_VERT - BORDERSIZE - BAR_INDICATOR_HEIGHT - pMonitor->vecPosition.y + offset.y, m_fBarWidth, BAR_INDICATOR_HEIGHT};
+        CBox rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x,
+                     ASSIGNEDBOX.y + ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT - pMonitor->vecPosition.y + offset.y, m_fBarWidth, BAR_INDICATOR_HEIGHT};
 
         if (rect.width <= 0 || rect.height <= 0)
             break;
@@ -123,6 +118,9 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
 
         // render title if necessary
         if (*PRENDERTITLES) {
+            CBox       rect = {ASSIGNEDBOX.x + xoff - pMonitor->vecPosition.x + offset.x, ASSIGNEDBOX.y - pMonitor->vecPosition.y + offset.y + BAR_PADDING_OUTER_VERT, m_fBarWidth,
+                         ASSIGNEDBOX.h - BAR_INDICATOR_HEIGHT - BAR_PADDING_OUTER_VERT * 2};
+
             CTitleTex* pTitleTex = textureFromTitle(m_dwGroupMembers[i]->m_szTitle);
 
             if (!pTitleTex)
@@ -131,17 +129,15 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
                                                                           Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}))
                                 .get();
 
-            rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * 0.8 * pMonitor->scale;
-
-            rect.y -= rect.height;
-            rect.width = m_fBarWidth * pMonitor->scale;
-
             refreshGradients();
 
-            if (*PGRADIENTS)
-                g_pHyprOpenGL->renderTexture((m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? m_tGradientActive : m_tGradientInactive), &rect, 1.0);
+            if (*PGRADIENTS) {
+                CBox rect2 = rect;
+                rect2.scale(pMonitor->scale);
+                g_pHyprOpenGL->renderTexture((m_dwGroupMembers[i] == g_pCompositor->m_pLastWindow ? m_tGradientActive : m_tGradientInactive), &rect2, 1.0);
+            }
 
-            rect.y -= (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * 0.2 * pMonitor->scale;
+            rect.y      = ASSIGNEDBOX.y + ASSIGNEDBOX.h / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0;
             rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale;
 
             g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f);
@@ -154,12 +150,6 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a, const Vector2D&
         invalidateTextures();
 }
 
-SWindowDecorationExtents CHyprGroupBarDecoration::getWindowDecorationReservedArea() {
-    static auto* const PRENDERTITLES  = &g_pConfigManager->getConfigValuePtr("group:groupbar:render_titles")->intValue;
-    static auto* const PTITLEFONTSIZE = &g_pConfigManager->getConfigValuePtr("group:groupbar:font_size")->intValue;
-    return SWindowDecorationExtents{{0, BAR_INDICATOR_HEIGHT + BAR_PADDING_OUTER_VERT * 2 + (*PRENDERTITLES ? *PTITLEFONTSIZE : 0)}, {}};
-}
-
 CTitleTex* CHyprGroupBarDecoration::textureFromTitle(const std::string& title) {
     for (auto& tex : m_sTitleTexs.titleTexs) {
         if (tex->szContent == title)
@@ -307,7 +297,7 @@ bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(CWindow* pDraggedWindow, con
     if (!pDraggedWindow->canBeGroupedInto(m_pWindow))
         return true;
 
-    const float BARRELATIVEX = pos.x - m_vLastWindowPos.x - m_fBarWidth / 2;
+    const float BARRELATIVEX = pos.x - assignedBoxGlobal().x - m_fBarWidth / 2;
     const int   WINDOWINDEX  = BARRELATIVEX < 0 ? -1 : (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
 
     CWindow*    pWindowInsertAfter = m_pWindow->getGroupWindowByIndex(WINDOWINDEX);
@@ -363,7 +353,7 @@ void CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, wlr_point
     if (e->state != WLR_BUTTON_PRESSED)
         return;
 
-    const float BARRELATIVEX = pos.x - m_vLastWindowPos.x;
+    const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
     const int   WINDOWINDEX  = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
 
     if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth) {
@@ -382,7 +372,7 @@ void CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, wlr_point
 }
 
 void CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
-    const float BARRELATIVEX = pos.x - m_vLastWindowPos.x;
+    const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
     const int   WINDOWINDEX  = (BARRELATIVEX) / (m_fBarWidth + BAR_HORIZONTAL_PADDING);
 
     if (BARRELATIVEX - (m_fBarWidth + BAR_HORIZONTAL_PADDING) * WINDOWINDEX > m_fBarWidth)
@@ -411,4 +401,9 @@ eDecorationLayer CHyprGroupBarDecoration::getDecorationLayer() {
 
 uint64_t CHyprGroupBarDecoration::getDecorationFlags() {
     return DECORATION_ALLOWS_MOUSE_INPUT;
+}
+
+CBox CHyprGroupBarDecoration::assignedBoxGlobal() {
+    CBox box = m_bAssignedBox;
+    return box.translate(g_pDecorationPositioner->getEdgeDefinedPoint(DECORATION_EDGE_TOP, m_pWindow));
 }
\ No newline at end of file
diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp
index 569fda212..52439942b 100644
--- a/src/render/decorations/CHyprGroupBarDecoration.hpp
+++ b/src/render/decorations/CHyprGroupBarDecoration.hpp
@@ -21,35 +21,34 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration {
     CHyprGroupBarDecoration(CWindow*);
     virtual ~CHyprGroupBarDecoration();
 
-    virtual SWindowDecorationExtents getWindowDecorationExtents();
+    virtual SDecorationPositioningInfo getPositioningInfo();
 
-    virtual void                     draw(CMonitor*, float a, const Vector2D& offset);
+    virtual void                       onPositioningReply(const SDecorationPositioningReply& reply);
 
-    virtual eDecorationType          getDecorationType();
+    virtual void                       draw(CMonitor*, float a, const Vector2D& offset);
 
-    virtual void                     updateWindow(CWindow*);
+    virtual eDecorationType            getDecorationType();
 
-    virtual void                     damageEntire();
+    virtual void                       updateWindow(CWindow*);
 
-    virtual SWindowDecorationExtents getWindowDecorationReservedArea();
+    virtual void                       damageEntire();
 
-    virtual void                     onBeginWindowDragOnDeco(const Vector2D&);
+    virtual void                       onBeginWindowDragOnDeco(const Vector2D&);
 
-    virtual bool                     onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&);
+    virtual bool                       onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&);
 
-    virtual void                     onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*);
+    virtual void                       onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*);
 
-    virtual eDecorationLayer         getDecorationLayer();
+    virtual eDecorationLayer           getDecorationLayer();
 
-    virtual uint64_t                 getDecorationFlags();
+    virtual uint64_t                   getDecorationFlags();
 
   private:
     SWindowDecorationExtents m_seExtents;
 
-    CWindow*                 m_pWindow = nullptr;
+    CBox                     m_bAssignedBox = {0};
 
-    Vector2D                 m_vLastWindowPos;
-    Vector2D                 m_vLastWindowSize;
+    CWindow*                 m_pWindow = nullptr;
 
     std::deque<CWindow*>     m_dwGroupMembers;
 
@@ -60,6 +59,8 @@ class CHyprGroupBarDecoration : public IHyprWindowDecoration {
 
     void                     refreshGradients();
 
+    CBox                     assignedBoxGlobal();
+
     struct STitleTexs {
         // STitleTexs*                            overriden = nullptr; // TODO: make shit shared in-group to decrease VRAM usage.
         std::deque<std::unique_ptr<CTitleTex>> titleTexs;
diff --git a/src/render/decorations/DecorationPositioner.cpp b/src/render/decorations/DecorationPositioner.cpp
new file mode 100644
index 000000000..5a8a42754
--- /dev/null
+++ b/src/render/decorations/DecorationPositioner.cpp
@@ -0,0 +1,277 @@
+#include "DecorationPositioner.hpp"
+#include "../../Compositor.hpp"
+
+CDecorationPositioner::CDecorationPositioner() {
+    g_pHookSystem->hookDynamic("closeWindow", [this](void* call, SCallbackInfo& info, std::any data) {
+        auto* const PWINDOW = std::any_cast<CWindow*>(data);
+        this->onWindowUnmap(PWINDOW);
+    });
+}
+
+Vector2D CDecorationPositioner::getEdgeDefinedPoint(uint32_t edges, CWindow* pWindow) {
+    const bool TOP    = edges & DECORATION_EDGE_TOP;
+    const bool BOTTOM = edges & DECORATION_EDGE_BOTTOM;
+    const bool LEFT   = edges & DECORATION_EDGE_LEFT;
+    const bool RIGHT  = edges & DECORATION_EDGE_RIGHT;
+
+    const int  EDGESNO = TOP + BOTTOM + LEFT + RIGHT;
+
+    if (EDGESNO == 0 || EDGESNO > 2) {
+        Debug::log(ERR, "getEdgeDefinedPoint: invalid number of edges");
+        return {};
+    }
+
+    CBox       wb         = pWindow->getWindowMainSurfaceBox();
+    const auto BORDERSIZE = pWindow->getRealBorderSize();
+    wb.expand(BORDERSIZE);
+
+    if (EDGESNO == 1) {
+        if (TOP)
+            return wb.pos() + Vector2D{wb.size().x / 2.0, 0};
+        else if (BOTTOM)
+            return wb.pos() + Vector2D{wb.size().x / 2.0, wb.size().y};
+        else if (LEFT)
+            return wb.pos() + Vector2D{0, wb.size().y / 2.0};
+        else if (RIGHT)
+            return wb.pos() + Vector2D{wb.size().x, wb.size().y / 2.0};
+        UNREACHABLE();
+    } else {
+        if (TOP && LEFT)
+            return wb.pos();
+        if (TOP && RIGHT)
+            return wb.pos() + Vector2D{wb.size().x, 0};
+        if (BOTTOM && RIGHT)
+            return wb.pos() + wb.size();
+        if (BOTTOM && LEFT)
+            return wb.pos() + Vector2D{0, wb.size().y};
+        UNREACHABLE();
+    }
+    UNREACHABLE();
+    return {};
+}
+
+void CDecorationPositioner::uncacheDecoration(IHyprWindowDecoration* deco) {
+    std::erase_if(m_vWindowPositioningDatas, [&](const auto& data) { return data->pDecoration == deco; });
+}
+
+void CDecorationPositioner::repositionDeco(IHyprWindowDecoration* deco) {
+    uncacheDecoration(deco);
+    onWindowUpdate(deco->m_pWindow);
+}
+
+CDecorationPositioner::SWindowPositioningData* CDecorationPositioner::getDataFor(IHyprWindowDecoration* pDecoration, CWindow* pWindow) {
+    auto it = std::find_if(m_vWindowPositioningDatas.begin(), m_vWindowPositioningDatas.end(), [&](const auto& el) { return el->pDecoration == pDecoration; });
+
+    if (it != m_vWindowPositioningDatas.end())
+        return it->get();
+
+    const auto DATA = m_vWindowPositioningDatas.emplace_back(std::make_unique<CDecorationPositioner::SWindowPositioningData>(pWindow, pDecoration)).get();
+
+    DATA->positioningInfo = pDecoration->getPositioningInfo();
+
+    return DATA;
+}
+
+void CDecorationPositioner::onWindowUpdate(CWindow* pWindow) {
+    if (!g_pCompositor->windowValidMapped(pWindow))
+        return;
+
+    auto* const WINDOWDATA = &m_mWindowDatas[pWindow];
+
+    //
+    std::vector<CDecorationPositioner::SWindowPositioningData*> datas;
+    for (auto& wd : pWindow->m_dWindowDecorations) {
+        datas.push_back(getDataFor(wd.get(), pWindow));
+    }
+
+    if (WINDOWDATA->lastWindowSize == pWindow->m_vRealSize.vec() /* position not changed */
+        &&
+        std::all_of(m_vWindowPositioningDatas.begin(), m_vWindowPositioningDatas.end(), [pWindow](const auto& data) { return pWindow != data->pWindow || !data->needsReposition; })
+        /* all window datas are either not for this window or don't need a reposition */
+    )
+        return;
+
+    WINDOWDATA->lastWindowSize = pWindow->m_vRealSize.vec();
+    const bool EPHEMERAL       = pWindow->m_vRealSize.isBeingAnimated();
+
+    std::sort(datas.begin(), datas.end(), [](const auto& a, const auto& b) { return a->positioningInfo.priority > b->positioningInfo.priority; });
+
+    CBox       wb         = pWindow->getWindowMainSurfaceBox();
+    const auto BORDERSIZE = pWindow->getRealBorderSize();
+    wb.expand(BORDERSIZE);
+
+    // calc reserved
+    float reservedXL = 0, reservedYT = 0, reservedXR = 0, reservedYB = 0;
+    for (size_t i = 0; i < datas.size(); ++i) {
+        auto* const wd = datas[i];
+
+        if (!wd->positioningInfo.reserved)
+            continue;
+
+        const bool TOP    = wd->positioningInfo.edges & DECORATION_EDGE_TOP;
+        const bool BOTTOM = wd->positioningInfo.edges & DECORATION_EDGE_BOTTOM;
+        const bool LEFT   = wd->positioningInfo.edges & DECORATION_EDGE_LEFT;
+        const bool RIGHT  = wd->positioningInfo.edges & DECORATION_EDGE_RIGHT;
+
+        if (LEFT)
+            reservedXL += wd->positioningInfo.desiredExtents.topLeft.x;
+        if (RIGHT)
+            reservedXR += wd->positioningInfo.desiredExtents.bottomRight.x;
+        if (TOP)
+            reservedYT += wd->positioningInfo.desiredExtents.topLeft.y;
+        if (BOTTOM)
+            reservedYB += wd->positioningInfo.desiredExtents.bottomRight.y;
+    }
+
+    WINDOWDATA->reserved = {{reservedXL, reservedYT}, {reservedXR, reservedYB}};
+
+    float stickyOffsetXL = 0, stickyOffsetYT = 0, stickyOffsetXR = 0, stickyOffsetYB = 0;
+
+    for (size_t i = 0; i < datas.size(); ++i) {
+        auto* const wd = datas[i];
+
+        wd->needsReposition = false;
+
+        const bool TOP     = wd->positioningInfo.edges & DECORATION_EDGE_TOP;
+        const bool BOTTOM  = wd->positioningInfo.edges & DECORATION_EDGE_BOTTOM;
+        const bool LEFT    = wd->positioningInfo.edges & DECORATION_EDGE_LEFT;
+        const bool RIGHT   = wd->positioningInfo.edges & DECORATION_EDGE_RIGHT;
+        const int  EDGESNO = TOP + BOTTOM + LEFT + RIGHT;
+
+        if (wd->positioningInfo.policy == DECORATION_POSITION_ABSOLUTE) {
+            if (LEFT)
+                stickyOffsetXL += wd->positioningInfo.desiredExtents.topLeft.x;
+            if (RIGHT)
+                stickyOffsetXR += wd->positioningInfo.desiredExtents.bottomRight.x;
+            if (TOP)
+                stickyOffsetYT += wd->positioningInfo.desiredExtents.topLeft.y;
+            if (BOTTOM)
+                stickyOffsetYB += wd->positioningInfo.desiredExtents.bottomRight.y;
+
+            wd->lastReply = {};
+            wd->pDecoration->onPositioningReply({});
+            continue;
+        }
+
+        if (wd->positioningInfo.policy == DECORATION_POSITION_STICKY) {
+            if (EDGESNO != 1) {
+                wd->lastReply = {};
+                wd->pDecoration->onPositioningReply({});
+                continue;
+            }
+
+            auto desiredSize = 0;
+            if (LEFT)
+                desiredSize = wd->positioningInfo.desiredExtents.topLeft.x;
+            else if (RIGHT)
+                desiredSize = wd->positioningInfo.desiredExtents.bottomRight.x;
+            else if (TOP)
+                desiredSize = wd->positioningInfo.desiredExtents.topLeft.y;
+            else
+                desiredSize = wd->positioningInfo.desiredExtents.bottomRight.y;
+
+            const auto EDGEPOINT = getEdgeDefinedPoint(wd->positioningInfo.edges, pWindow);
+
+            Vector2D   pos, size;
+
+            if (LEFT) {
+                pos = wb.pos() - EDGEPOINT - Vector2D{stickyOffsetXL, 0};
+                pos.x -= desiredSize;
+                size = {desiredSize, wb.size().y};
+
+                stickyOffsetXL += desiredSize;
+            } else if (RIGHT) {
+                pos  = wb.pos() + Vector2D{wb.size().x, 0} - EDGEPOINT + Vector2D{stickyOffsetXR, 0};
+                size = {desiredSize, wb.size().y};
+
+                stickyOffsetXR += desiredSize;
+            } else if (TOP) {
+                pos = wb.pos() - EDGEPOINT - Vector2D{0, stickyOffsetYT};
+                pos.y -= desiredSize;
+                size = {wb.size().x, desiredSize};
+
+                stickyOffsetYT += desiredSize;
+            } else {
+                pos  = wb.pos() + Vector2D{0, wb.size().y} - EDGEPOINT - Vector2D{0, stickyOffsetYB};
+                size = {wb.size().x, desiredSize};
+
+                stickyOffsetYB += desiredSize;
+            }
+
+            wd->lastReply = {{pos, size}, EPHEMERAL};
+            wd->pDecoration->onPositioningReply(wd->lastReply);
+
+            continue;
+        } else {
+            // invalid
+            wd->lastReply = {};
+            wd->pDecoration->onPositioningReply({});
+            continue;
+        }
+    }
+
+    WINDOWDATA->extents = {{stickyOffsetXL + reservedXL, stickyOffsetYT + reservedYT}, {stickyOffsetXR + reservedXR, stickyOffsetYB + reservedYB}};
+}
+
+void CDecorationPositioner::onWindowUnmap(CWindow* pWindow) {
+    std::erase_if(m_vWindowPositioningDatas, [&](const auto& data) { return data->pWindow == pWindow; });
+    m_mWindowDatas.erase(pWindow);
+}
+
+SWindowDecorationExtents CDecorationPositioner::getWindowDecorationReserved(CWindow* pWindow) {
+    return m_mWindowDatas[pWindow].reserved;
+}
+
+SWindowDecorationExtents CDecorationPositioner::getWindowDecorationExtents(CWindow* pWindow, bool inputOnly) {
+    if (!inputOnly)
+        return m_mWindowDatas[pWindow].extents;
+
+    // TODO:
+    return m_mWindowDatas[pWindow].extents;
+}
+
+CBox CDecorationPositioner::getBoxWithIncludedDecos(CWindow* pWindow) {
+    CBox accum = pWindow->getWindowMainSurfaceBox().expand(pWindow->getRealBorderSize());
+
+    for (auto& data : m_vWindowPositioningDatas) {
+        if (data->pWindow != pWindow)
+            continue;
+
+        if (!(data->pDecoration->getDecorationFlags() & DECORATION_PART_OF_MAIN_WINDOW))
+            continue;
+
+        CBox decoBox;
+
+        if (data->positioningInfo.policy == DECORATION_POSITION_ABSOLUTE) {
+            decoBox = data->pWindow->getWindowMainSurfaceBox();
+            decoBox.addExtents(data->positioningInfo.desiredExtents);
+        } else {
+            decoBox              = data->lastReply.assignedGeometry;
+            const auto EDGEPOINT = getEdgeDefinedPoint(data->positioningInfo.edges, pWindow);
+            decoBox.translate(EDGEPOINT);
+        }
+
+        SWindowDecorationExtents extentsToAdd;
+
+        if (decoBox.x < accum.x)
+            extentsToAdd.topLeft.x = accum.x - decoBox.x;
+        if (decoBox.y < accum.y)
+            extentsToAdd.topLeft.y = accum.y - decoBox.y;
+        if (decoBox.x + decoBox.w > accum.x + accum.w)
+            extentsToAdd.bottomRight.x = accum.x + accum.w - (decoBox.x + decoBox.w);
+        if (decoBox.y + decoBox.h > accum.y + accum.h)
+            extentsToAdd.bottomRight.y = accum.y + accum.h - (decoBox.y + decoBox.h);
+
+        accum.addExtents(extentsToAdd);
+    }
+
+    return accum;
+}
+
+CBox CDecorationPositioner::getWindowDecorationBox(IHyprWindowDecoration* deco) {
+    const auto DATA = getDataFor(deco, deco->m_pWindow);
+
+    CBox       box = DATA->lastReply.assignedGeometry;
+    box.translate(getEdgeDefinedPoint(DATA->positioningInfo.edges, deco->m_pWindow));
+    return box;
+}
\ No newline at end of file
diff --git a/src/render/decorations/DecorationPositioner.hpp b/src/render/decorations/DecorationPositioner.hpp
new file mode 100644
index 000000000..c85bc2170
--- /dev/null
+++ b/src/render/decorations/DecorationPositioner.hpp
@@ -0,0 +1,96 @@
+#pragma once
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+#include "../../helpers/Box.hpp"
+
+class CWindow;
+class IHyprWindowDecoration;
+
+enum eDecorationPositioningPolicy
+{
+    DECORATION_POSITION_ABSOLUTE = 0, /* Decoration does not interfere with anything else */
+    DECORATION_POSITION_STICKY,       /* Decoration is stuck to some edge of a window */
+};
+
+enum eDecorationEdges
+{
+    DECORATION_EDGE_TOP    = 1 << 0,
+    DECORATION_EDGE_BOTTOM = 1 << 1,
+    DECORATION_EDGE_LEFT   = 1 << 2,
+    DECORATION_EDGE_RIGHT  = 1 << 3
+};
+
+/*
+Request the positioner to position a decoration
+
+DECORATION_POSITION_ABSOLUTE:
+    - desiredExtents may contain the extents to be used when reserved is set. Edges has to have the edges used.
+DECORATION_POSITION_STICKY:
+    - one edge allowed
+    - priority allowed
+    - desiredExtents contains the desired extents. Any other edge than the one selected is ignored.
+    - reserved is allowed
+*/
+struct SDecorationPositioningInfo {
+    eDecorationPositioningPolicy policy   = DECORATION_POSITION_ABSOLUTE;
+    uint32_t                     edges    = 0;  // enum eDecorationEdges
+    uint32_t                     priority = 10; // priority, decos will be evaluated high -> low
+    SWindowDecorationExtents     desiredExtents;
+    bool                         reserved = false; // if true, geometry will use reserved area
+};
+
+/*
+A reply from the positioner. This may be sent multiple times, if anything changes.
+
+DECORATION_POSITION_ABSOLUTE:
+    - assignedGeometry is empty
+DECORATION_POSITION_STICKY:
+    - assignedGeometry is relative to the edge's center point
+    - ephemeral is sent
+*/
+struct SDecorationPositioningReply {
+    CBox assignedGeometry;
+    bool ephemeral = false; // if true, means it's a result of an animation and will change soon.
+};
+
+class CDecorationPositioner {
+  public:
+    CDecorationPositioner();
+
+    Vector2D getEdgeDefinedPoint(uint32_t edges, CWindow* pWindow);
+
+    // called on resize, or insert/removal of a new deco
+    void                     onWindowUpdate(CWindow* pWindow);
+    void                     uncacheDecoration(IHyprWindowDecoration* deco);
+    SWindowDecorationExtents getWindowDecorationReserved(CWindow* pWindow);
+    SWindowDecorationExtents getWindowDecorationExtents(CWindow* pWindow, bool inputOnly = false);
+    CBox                     getBoxWithIncludedDecos(CWindow* pWindow);
+    void                     repositionDeco(IHyprWindowDecoration* deco);
+    CBox                     getWindowDecorationBox(IHyprWindowDecoration* deco);
+
+  private:
+    struct SWindowPositioningData {
+        CWindow*                    pWindow     = nullptr;
+        IHyprWindowDecoration*      pDecoration = nullptr;
+        SDecorationPositioningInfo  positioningInfo;
+        SDecorationPositioningReply lastReply;
+        bool                        needsReposition = true;
+    };
+
+    struct SWindowData {
+        Vector2D                 lastWindowSize = {};
+        SWindowDecorationExtents reserved       = {};
+        SWindowDecorationExtents extents        = {};
+    };
+
+    std::unordered_map<CWindow*, SWindowData>            m_mWindowDatas;
+    std::vector<std::unique_ptr<SWindowPositioningData>> m_vWindowPositioningDatas;
+
+    SWindowPositioningData*                              getDataFor(IHyprWindowDecoration* pDecoration, CWindow* pWindow);
+    void                                                 onWindowUnmap(CWindow* pWindow);
+};
+
+inline std::unique_ptr<CDecorationPositioner> g_pDecorationPositioner;
\ No newline at end of file
diff --git a/src/render/decorations/IHyprWindowDecoration.cpp b/src/render/decorations/IHyprWindowDecoration.cpp
index d2b9b0f9e..7a4906b56 100644
--- a/src/render/decorations/IHyprWindowDecoration.cpp
+++ b/src/render/decorations/IHyprWindowDecoration.cpp
@@ -8,23 +8,6 @@ IHyprWindowDecoration::IHyprWindowDecoration(CWindow* pWindow) {
 
 IHyprWindowDecoration::~IHyprWindowDecoration() {}
 
-SWindowDecorationExtents IHyprWindowDecoration::getWindowDecorationReservedArea() {
-    return SWindowDecorationExtents{};
-}
-
-CRegion IHyprWindowDecoration::getWindowDecorationRegion() {
-    const SWindowDecorationExtents RESERVED   = getWindowDecorationReservedArea();
-    const int                      BORDERSIZE = m_pWindow->getRealBorderSize();
-    return CRegion(m_pWindow->m_vRealPosition.vec().x - (BORDERSIZE + RESERVED.topLeft.x) * (int)(RESERVED.topLeft.x != 0),
-                   m_pWindow->m_vRealPosition.vec().y - (BORDERSIZE + RESERVED.topLeft.y) * (int)(RESERVED.topLeft.y != 0),
-                   m_pWindow->m_vRealSize.vec().x + (BORDERSIZE + RESERVED.topLeft.x) * (int)(RESERVED.topLeft.x != 0) +
-                       (BORDERSIZE + RESERVED.bottomRight.x) * (int)(RESERVED.bottomRight.x != 0),
-                   m_pWindow->m_vRealSize.vec().y + (BORDERSIZE + RESERVED.topLeft.y) * (int)(RESERVED.topLeft.y != 0) +
-                       (BORDERSIZE + RESERVED.bottomRight.y) * (int)(RESERVED.bottomRight.y != 0))
-        .subtract(CRegion(m_pWindow->m_vRealPosition.vec().x - BORDERSIZE, m_pWindow->m_vRealPosition.vec().y - BORDERSIZE, m_pWindow->m_vRealSize.vec().x + 2 * BORDERSIZE,
-                          m_pWindow->m_vRealSize.vec().y + 2 * BORDERSIZE));
-}
-
 void IHyprWindowDecoration::onBeginWindowDragOnDeco(const Vector2D&) {
     ;
 }
diff --git a/src/render/decorations/IHyprWindowDecoration.hpp b/src/render/decorations/IHyprWindowDecoration.hpp
index 47945b389..15890f44d 100644
--- a/src/render/decorations/IHyprWindowDecoration.hpp
+++ b/src/render/decorations/IHyprWindowDecoration.hpp
@@ -2,6 +2,7 @@
 
 #include "../../defines.hpp"
 #include "../../helpers/Region.hpp"
+#include "DecorationPositioner.hpp"
 
 enum eDecorationType
 {
@@ -27,36 +28,37 @@ enum eDecorationFlags
 
 class CWindow;
 class CMonitor;
+class CDecorationPositioner;
 
 class IHyprWindowDecoration {
   public:
     IHyprWindowDecoration(CWindow*);
     virtual ~IHyprWindowDecoration() = 0;
 
-    virtual SWindowDecorationExtents getWindowDecorationExtents() = 0;
+    virtual SDecorationPositioningInfo getPositioningInfo() = 0;
 
-    virtual void                     draw(CMonitor*, float a, const Vector2D& offset = Vector2D()) = 0;
+    virtual void                       onPositioningReply(const SDecorationPositioningReply& reply) = 0;
 
-    virtual eDecorationType          getDecorationType() = 0;
+    virtual void                       draw(CMonitor*, float a, const Vector2D& offset = Vector2D()) = 0;
 
-    virtual void                     updateWindow(CWindow*) = 0;
+    virtual eDecorationType            getDecorationType() = 0;
 
-    virtual void                     damageEntire() = 0;
+    virtual void                       updateWindow(CWindow*) = 0;
 
-    virtual SWindowDecorationExtents getWindowDecorationReservedArea();
+    virtual void                       damageEntire() = 0; // should be ignored by non-absolute decos
 
-    virtual CRegion                  getWindowDecorationRegion();
+    virtual void                       onBeginWindowDragOnDeco(const Vector2D&); // called when the user calls the "movewindow" mouse dispatcher on the deco
 
-    virtual void                     onBeginWindowDragOnDeco(const Vector2D&); // called when the user calls the "movewindow" mouse dispatcher on the deco
+    virtual bool                       onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&); // returns true if the window should be placed by the layout
 
-    virtual bool                     onEndWindowDragOnDeco(CWindow* pDraggedWindow, const Vector2D&); // returns true if the window should be placed by the layout
+    virtual void                       onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*);
 
-    virtual void                     onMouseButtonOnDeco(const Vector2D&, wlr_pointer_button_event*);
+    virtual eDecorationLayer           getDecorationLayer();
 
-    virtual eDecorationLayer         getDecorationLayer();
-
-    virtual uint64_t                 getDecorationFlags();
+    virtual uint64_t                   getDecorationFlags();
 
   private:
     CWindow* m_pWindow = nullptr;
+
+    friend class CDecorationPositioner;
 };