mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-13 14:05:59 +01:00
bca3068db2
* feat: add lockactivegroup dispatcher The `lockactivewindow` dispatcher takes `lock`, `toggle` or `unlock` as arguments. When a group is locked, no window or group can be added to it, nor can it be added to another group, but the `moveintogroup` and `moveoutofgroup` dispatches are not affected. Implementation details: the lock is implement via `SGroupData.locked` flag (defaults to false). The flag is only relevant to the group head, and upon the group head's succession, the flag will be passed down to the new head. Meanwhile, the old head's flag will be set to false. The flag is set to false when a group is dismissed. New condition checks have been added to the dwindle and master layout to check if target group is unlocked (and if the source is also a group and unlocked) before adding windows to the target group. * refactor: `lockactivegroup dispatcher code ordering
1123 lines
44 KiB
C++
1123 lines
44 KiB
C++
#include "MasterLayout.hpp"
|
|
#include "../Compositor.hpp"
|
|
#include <ranges>
|
|
|
|
SMasterNodeData* CHyprMasterLayout::getNodeFromWindow(CWindow* pWindow) {
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.pWindow == pWindow)
|
|
return &nd;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int CHyprMasterLayout::getNodesOnWorkspace(const int& ws) {
|
|
int no = 0;
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == ws)
|
|
no++;
|
|
}
|
|
|
|
return no;
|
|
}
|
|
|
|
int CHyprMasterLayout::getMastersOnWorkspace(const int& ws) {
|
|
int no = 0;
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == ws && n.isMaster)
|
|
no++;
|
|
}
|
|
|
|
return no;
|
|
}
|
|
|
|
SMasterWorkspaceData* CHyprMasterLayout::getMasterWorkspaceData(const int& ws) {
|
|
for (auto& n : m_lMasterWorkspacesData) {
|
|
if (n.workspaceID == ws)
|
|
return &n;
|
|
}
|
|
|
|
//create on the fly if it doesn't exist yet
|
|
const auto PWORKSPACEDATA = &m_lMasterWorkspacesData.emplace_back();
|
|
PWORKSPACEDATA->workspaceID = ws;
|
|
const auto orientation = &g_pConfigManager->getConfigValuePtr("master:orientation")->strValue;
|
|
if (*orientation == "top") {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_TOP;
|
|
} else if (*orientation == "right") {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_RIGHT;
|
|
} else if (*orientation == "bottom") {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_BOTTOM;
|
|
} else if (*orientation == "left") {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_LEFT;
|
|
} else {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_CENTER;
|
|
}
|
|
return PWORKSPACEDATA;
|
|
}
|
|
|
|
std::string CHyprMasterLayout::getLayoutName() {
|
|
return "Master";
|
|
}
|
|
|
|
SMasterNodeData* CHyprMasterLayout::getMasterNodeOnWorkspace(const int& ws) {
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.isMaster && n.workspaceID == ws)
|
|
return &n;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void CHyprMasterLayout::onWindowCreatedTiling(CWindow* pWindow) {
|
|
if (pWindow->m_bIsFloating)
|
|
return;
|
|
|
|
static auto* const PNEWTOP = &g_pConfigManager->getConfigValuePtr("master:new_on_top")->intValue;
|
|
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
|
|
|
const auto PNODE = *PNEWTOP ? &m_lMasterNodesData.emplace_front() : &m_lMasterNodesData.emplace_back();
|
|
|
|
PNODE->workspaceID = pWindow->m_iWorkspaceID;
|
|
PNODE->pWindow = pWindow;
|
|
|
|
static auto* const PNEWISMASTER = &g_pConfigManager->getConfigValuePtr("master:new_is_master")->intValue;
|
|
|
|
const auto WINDOWSONWORKSPACE = getNodesOnWorkspace(PNODE->workspaceID);
|
|
static auto* const PMFACT = &g_pConfigManager->getConfigValuePtr("master:mfact")->floatValue;
|
|
float lastSplitPercent = *PMFACT;
|
|
|
|
auto OPENINGON = isWindowTiled(g_pCompositor->m_pLastWindow) && g_pCompositor->m_pLastWindow->m_iWorkspaceID == pWindow->m_iWorkspaceID ?
|
|
getNodeFromWindow(g_pCompositor->m_pLastWindow) :
|
|
getMasterNodeOnWorkspace(pWindow->m_iWorkspaceID);
|
|
|
|
if (OPENINGON && OPENINGON->pWindow->m_sGroupData.pNextWindow && !OPENINGON->pWindow->getGroupHead()->m_sGroupData.locked && // target is an unlocked group
|
|
(!pWindow->m_sGroupData.pNextWindow || !pWindow->getGroupHead()->m_sGroupData.locked) // source is not group or is an unlocked group
|
|
&& OPENINGON != PNODE && !g_pKeybindManager->m_bGroupsLocked) {
|
|
m_lMasterNodesData.remove(*PNODE);
|
|
|
|
OPENINGON->pWindow->insertWindowToGroup(pWindow);
|
|
|
|
pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
|
|
|
|
return;
|
|
}
|
|
|
|
if (*PNEWISMASTER || WINDOWSONWORKSPACE == 1 || (!pWindow->m_bFirstMap && OPENINGON->isMaster)) {
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.isMaster && nd.workspaceID == PNODE->workspaceID) {
|
|
nd.isMaster = false;
|
|
lastSplitPercent = nd.percMaster;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PNODE->isMaster = true;
|
|
PNODE->percMaster = lastSplitPercent;
|
|
|
|
// first, check if it isn't too big.
|
|
if (const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(pWindow); MAXSIZE.x < PMONITOR->vecSize.x * lastSplitPercent || MAXSIZE.y < PMONITOR->vecSize.y) {
|
|
// we can't continue. make it floating.
|
|
pWindow->m_bIsFloating = true;
|
|
m_lMasterNodesData.remove(*PNODE);
|
|
g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow);
|
|
return;
|
|
}
|
|
} else {
|
|
PNODE->isMaster = false;
|
|
PNODE->percMaster = lastSplitPercent;
|
|
|
|
// first, check if it isn't too big.
|
|
if (const auto MAXSIZE = g_pXWaylandManager->getMaxSizeForWindow(pWindow);
|
|
MAXSIZE.x < PMONITOR->vecSize.x * (1 - lastSplitPercent) || MAXSIZE.y < PMONITOR->vecSize.y * (1.f / (WINDOWSONWORKSPACE - 1))) {
|
|
// we can't continue. make it floating.
|
|
pWindow->m_bIsFloating = true;
|
|
m_lMasterNodesData.remove(*PNODE);
|
|
g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(pWindow);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
|
|
|
|
if (PWORKSPACE->m_bHasFullscreenWindow) {
|
|
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
|
|
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, FULLSCREEN_FULL);
|
|
}
|
|
|
|
// recalc
|
|
recalculateMonitor(pWindow->m_iMonitorID);
|
|
}
|
|
|
|
void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) {
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
|
|
if (!PNODE)
|
|
return;
|
|
|
|
const auto WORKSPACEID = PNODE->workspaceID;
|
|
const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID);
|
|
static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue;
|
|
|
|
pWindow->m_sSpecialRenderData.rounding = true;
|
|
pWindow->m_sSpecialRenderData.border = true;
|
|
pWindow->m_sSpecialRenderData.decorate = true;
|
|
|
|
if (pWindow->m_bIsFullscreen)
|
|
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
|
|
|
if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) {
|
|
// find a new master from top of the list
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (!nd.isMaster && nd.workspaceID == WORKSPACEID) {
|
|
nd.isMaster = true;
|
|
nd.percMaster = PNODE->percMaster;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_lMasterNodesData.remove(*PNODE);
|
|
|
|
if (getMastersOnWorkspace(WORKSPACEID) == getNodesOnWorkspace(WORKSPACEID) && MASTERSLEFT > 1) {
|
|
for (auto& nd : m_lMasterNodesData | std::views::reverse) {
|
|
if (nd.workspaceID == WORKSPACEID) {
|
|
nd.isMaster = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// BUGFIX: correct bug where closing one master in a stack of 2 would leave
|
|
// the screen half bare, and make it difficult to select remaining window
|
|
if (getNodesOnWorkspace(WORKSPACEID) == 1) {
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.workspaceID == WORKSPACEID && nd.isMaster == false) {
|
|
nd.isMaster = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
recalculateMonitor(pWindow->m_iMonitorID);
|
|
}
|
|
|
|
void CHyprMasterLayout::recalculateMonitor(const int& monid) {
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(monid);
|
|
|
|
if (!PMONITOR)
|
|
return;
|
|
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
|
|
|
|
if (!PWORKSPACE)
|
|
return;
|
|
|
|
g_pHyprRenderer->damageMonitor(PMONITOR);
|
|
|
|
if (PMONITOR->specialWorkspaceID) {
|
|
calculateWorkspace(PMONITOR->specialWorkspaceID);
|
|
}
|
|
|
|
if (PWORKSPACE->m_bHasFullscreenWindow) {
|
|
// massive hack from the fullscreen func
|
|
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
|
|
|
|
if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
|
|
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
|
|
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
|
|
} else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
|
|
SMasterNodeData fakeNode;
|
|
fakeNode.pWindow = PFULLWINDOW;
|
|
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
|
|
fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight;
|
|
fakeNode.workspaceID = PWORKSPACE->m_iID;
|
|
PFULLWINDOW->m_vPosition = fakeNode.position;
|
|
PFULLWINDOW->m_vSize = fakeNode.size;
|
|
|
|
applyNodeDataToWindow(&fakeNode);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// calc the WS
|
|
calculateWorkspace(PWORKSPACE->m_iID);
|
|
}
|
|
|
|
void CHyprMasterLayout::calculateWorkspace(const int& ws) {
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(ws);
|
|
|
|
if (!PWORKSPACE)
|
|
return;
|
|
|
|
const auto PWORKSPACEDATA = getMasterWorkspaceData(ws);
|
|
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID);
|
|
|
|
const auto PMASTERNODE = getMasterNodeOnWorkspace(PWORKSPACE->m_iID);
|
|
|
|
if (!PMASTERNODE)
|
|
return;
|
|
|
|
eOrientation orientation = PWORKSPACEDATA->orientation;
|
|
bool centerMasterWindow = false;
|
|
static auto* const ALWAYSCENTER = &g_pConfigManager->getConfigValuePtr("master:always_center_master")->intValue;
|
|
if (orientation == ORIENTATION_CENTER) {
|
|
if (getNodesOnWorkspace(PWORKSPACE->m_iID) > 2 || (*ALWAYSCENTER == 1)) {
|
|
centerMasterWindow = true;
|
|
} else {
|
|
orientation = ORIENTATION_LEFT;
|
|
}
|
|
}
|
|
|
|
const auto MASTERS = getMastersOnWorkspace(PWORKSPACE->m_iID);
|
|
const auto WINDOWS = getNodesOnWorkspace(PWORKSPACE->m_iID);
|
|
const auto STACKWINDOWS = WINDOWS - MASTERS;
|
|
//compute placement of master window(s)
|
|
if ((WINDOWS < 2) && !centerMasterWindow) {
|
|
PMASTERNODE->position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition;
|
|
PMASTERNODE->size = Vector2D(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x,
|
|
PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y);
|
|
applyNodeDataToWindow(PMASTERNODE);
|
|
return;
|
|
} else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
|
|
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
|
|
int nodesLeft = MASTERS;
|
|
float nextY = 0;
|
|
float WIDTH = 0;
|
|
|
|
if (STACKWINDOWS == 0 && MASTERS > 0)
|
|
WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x);
|
|
else
|
|
WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) * PMASTERNODE->percMaster;
|
|
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == PWORKSPACE->m_iID && n.isMaster) {
|
|
if (orientation == ORIENTATION_RIGHT) {
|
|
n.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition +
|
|
Vector2D(PMONITOR->vecSize.x - WIDTH - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x, nextY);
|
|
} else {
|
|
n.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(0, nextY);
|
|
}
|
|
float HEIGHT = nodesLeft > 1 ? heightLeft / nodesLeft * n.percSize : heightLeft;
|
|
if (HEIGHT > heightLeft * 0.9f && nodesLeft > 1)
|
|
HEIGHT = heightLeft * 0.9f;
|
|
n.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
nodesLeft--;
|
|
heightLeft -= HEIGHT;
|
|
nextY += HEIGHT;
|
|
|
|
applyNodeDataToWindow(&n);
|
|
}
|
|
}
|
|
} else if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) {
|
|
float widthLeft = PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x;
|
|
int nodesLeft = MASTERS;
|
|
float nextX = 0;
|
|
const float HEIGHT = (PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y) * PMASTERNODE->percMaster;
|
|
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == PWORKSPACE->m_iID && n.isMaster) {
|
|
if (orientation == ORIENTATION_BOTTOM) {
|
|
n.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition +
|
|
Vector2D(nextX, PMONITOR->vecSize.y - HEIGHT - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y);
|
|
} else {
|
|
n.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(nextX, 0);
|
|
}
|
|
float WIDTH = nodesLeft > 1 ? widthLeft / nodesLeft * n.percSize : widthLeft;
|
|
if (WIDTH > widthLeft * 0.9f && nodesLeft > 1)
|
|
WIDTH = widthLeft * 0.9f;
|
|
n.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
nodesLeft--;
|
|
widthLeft -= WIDTH;
|
|
nextX += WIDTH;
|
|
|
|
applyNodeDataToWindow(&n);
|
|
}
|
|
}
|
|
} else if (orientation == ORIENTATION_CENTER && STACKWINDOWS >= 2) {
|
|
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
|
|
int nodesLeft = MASTERS;
|
|
float nextY = 0;
|
|
const float WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) * PMASTERNODE->percMaster;
|
|
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == PWORKSPACE->m_iID && n.isMaster) {
|
|
float CENTER_OFFSET = (PMONITOR->vecSize.x - WIDTH) / 2;
|
|
n.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(CENTER_OFFSET, nextY);
|
|
float HEIGHT = nodesLeft > 1 ? heightLeft / nodesLeft * n.percSize : heightLeft;
|
|
if (HEIGHT > heightLeft * 0.9f && nodesLeft > 1)
|
|
HEIGHT = heightLeft * 0.9f;
|
|
n.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
nodesLeft--;
|
|
heightLeft -= HEIGHT;
|
|
nextY += HEIGHT;
|
|
|
|
applyNodeDataToWindow(&n);
|
|
}
|
|
}
|
|
}
|
|
|
|
//compute placement of slave window(s)
|
|
int slavesLeft = getNodesOnWorkspace(PWORKSPACE->m_iID) - MASTERS;
|
|
if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
|
|
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
|
|
float nextY = 0;
|
|
const float WIDTH = PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x - PMASTERNODE->size.x;
|
|
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.workspaceID != PWORKSPACE->m_iID || nd.isMaster)
|
|
continue;
|
|
|
|
if (orientation == ORIENTATION_LEFT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition +
|
|
Vector2D(PMASTERNODE->percMaster * (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x), nextY);
|
|
} else {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(0, nextY);
|
|
}
|
|
float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd.percSize : heightLeft;
|
|
if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1)
|
|
HEIGHT = heightLeft * 0.9f;
|
|
nd.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
slavesLeft--;
|
|
heightLeft -= HEIGHT;
|
|
nextY += HEIGHT;
|
|
|
|
applyNodeDataToWindow(&nd);
|
|
}
|
|
} else if (orientation == ORIENTATION_TOP || orientation == ORIENTATION_BOTTOM) {
|
|
float widthLeft = PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x;
|
|
float nextX = 0;
|
|
const float HEIGHT = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y - PMASTERNODE->size.y;
|
|
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.workspaceID != PWORKSPACE->m_iID || nd.isMaster)
|
|
continue;
|
|
if (orientation == ORIENTATION_TOP) {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition +
|
|
Vector2D(nextX, PMASTERNODE->percMaster * (PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y));
|
|
} else {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(nextX, 0);
|
|
}
|
|
float WIDTH = slavesLeft > 1 ? widthLeft / slavesLeft * nd.percSize : widthLeft;
|
|
if (WIDTH > widthLeft * 0.9f && slavesLeft > 1)
|
|
WIDTH = widthLeft * 0.9f;
|
|
nd.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
slavesLeft--;
|
|
widthLeft -= WIDTH;
|
|
nextX += WIDTH;
|
|
|
|
applyNodeDataToWindow(&nd);
|
|
}
|
|
} else if (orientation == ORIENTATION_CENTER && STACKWINDOWS >= 2) {
|
|
float heightLeftL = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
|
|
float heightLeftR = heightLeftL;
|
|
float heightLeft = 0;
|
|
float nextYL = 0;
|
|
float nextYR = 0;
|
|
const float WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x - PMASTERNODE->size.x) / 2.0;
|
|
bool on_left = true;
|
|
|
|
int slavesLeftL = 1 + (slavesLeft - 1) / 2;
|
|
int slavesLeftR = slavesLeft - slavesLeftL;
|
|
|
|
for (auto& nd : m_lMasterNodesData) {
|
|
if (nd.workspaceID != PWORKSPACE->m_iID || nd.isMaster)
|
|
continue;
|
|
|
|
if (on_left) {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(0, nextYL);
|
|
heightLeft = heightLeftL;
|
|
slavesLeft = slavesLeftL;
|
|
} else {
|
|
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition + Vector2D(WIDTH + PMASTERNODE->size.x, nextYR);
|
|
heightLeft = heightLeftR;
|
|
slavesLeft = slavesLeftR;
|
|
}
|
|
float HEIGHT = slavesLeft > 1 ? heightLeft / slavesLeft * nd.percSize : heightLeft;
|
|
if (HEIGHT > heightLeft * 0.9f && slavesLeft > 1)
|
|
HEIGHT = heightLeft * 0.9f;
|
|
nd.size = Vector2D(WIDTH, HEIGHT);
|
|
|
|
if (on_left) {
|
|
heightLeftL -= HEIGHT;
|
|
nextYL += HEIGHT;
|
|
slavesLeftL--;
|
|
} else {
|
|
heightLeftR -= HEIGHT;
|
|
nextYR += HEIGHT;
|
|
slavesLeftR--;
|
|
}
|
|
|
|
applyNodeDataToWindow(&nd);
|
|
on_left = !on_left;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
|
|
CMonitor* PMONITOR = nullptr;
|
|
|
|
if (g_pCompositor->isWorkspaceSpecial(pNode->workspaceID)) {
|
|
for (auto& m : g_pCompositor->m_vMonitors) {
|
|
if (m->specialWorkspaceID == pNode->workspaceID) {
|
|
PMONITOR = m.get();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(pNode->workspaceID)->m_iMonitorID);
|
|
}
|
|
|
|
if (!PMONITOR) {
|
|
Debug::log(ERR, "Orphaned Node %lx (workspace ID: %i)!!", pNode, pNode->workspaceID);
|
|
return;
|
|
}
|
|
|
|
// for gaps outer
|
|
const bool DISPLAYLEFT = STICKS(pNode->position.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x);
|
|
const bool DISPLAYRIGHT = STICKS(pNode->position.x + pNode->size.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x);
|
|
const bool DISPLAYTOP = STICKS(pNode->position.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y);
|
|
const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y);
|
|
|
|
const auto PWINDOW = pNode->pWindow;
|
|
// get specific gaps and rules for this workspace,
|
|
// if user specified them in config
|
|
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID));
|
|
|
|
static auto* const PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
|
|
static auto* const PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
|
|
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
|
|
|
|
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
|
|
auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
|
|
auto borderSize =
|
|
PWINDOW->m_sSpecialRenderData.borderSize.toUnderlying() != -1 ? PWINDOW->m_sSpecialRenderData.borderSize.toUnderlying() : WORKSPACERULE.borderSize.value_or(*PBORDERSIZE);
|
|
|
|
if (!g_pCompositor->windowValidMapped(PWINDOW)) {
|
|
Debug::log(ERR, "Node %lx holding invalid window %lx!!", pNode, PWINDOW);
|
|
return;
|
|
}
|
|
|
|
static auto* const PNOGAPSWHENONLY = &g_pConfigManager->getConfigValuePtr("master:no_gaps_when_only")->intValue;
|
|
|
|
PWINDOW->m_vSize = pNode->size;
|
|
PWINDOW->m_vPosition = pNode->position;
|
|
|
|
auto calcPos = PWINDOW->m_vPosition + Vector2D(borderSize, borderSize);
|
|
auto calcSize = PWINDOW->m_vSize - Vector2D(2 * borderSize, 2 * borderSize);
|
|
|
|
if (*PNOGAPSWHENONLY && !WORKSPACERULE.border && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) &&
|
|
(getNodesOnWorkspace(PWINDOW->m_iWorkspaceID) == 1 ||
|
|
(PWINDOW->m_bIsFullscreen && g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
|
|
PWINDOW->m_vRealPosition = calcPos - Vector2D(borderSize, borderSize);
|
|
PWINDOW->m_vRealSize = calcSize + Vector2D(2 * borderSize, 2 * borderSize);
|
|
|
|
PWINDOW->updateWindowDecos();
|
|
|
|
PWINDOW->m_sSpecialRenderData.rounding = false;
|
|
PWINDOW->m_sSpecialRenderData.border = false;
|
|
PWINDOW->m_sSpecialRenderData.decorate = false;
|
|
|
|
return;
|
|
}
|
|
|
|
PWINDOW->m_sSpecialRenderData.rounding = WORKSPACERULE.rounding.value_or(true);
|
|
PWINDOW->m_sSpecialRenderData.decorate = WORKSPACERULE.decorate.value_or(true);
|
|
PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(true);
|
|
PWINDOW->m_sSpecialRenderData.borderSize = WORKSPACERULE.borderSize.value_or(-1);
|
|
|
|
const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut : gapsIn, DISPLAYTOP ? gapsOut : gapsIn);
|
|
|
|
const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut : gapsIn, DISPLAYBOTTOM ? gapsOut : gapsIn);
|
|
|
|
calcPos = calcPos + OFFSETTOPLEFT;
|
|
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
|
|
|
|
const auto RESERVED = PWINDOW->getFullWindowReservedArea();
|
|
calcPos = calcPos + RESERVED.topLeft;
|
|
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
|
|
|
if (g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID)) {
|
|
static auto* const PSCALEFACTOR = &g_pConfigManager->getConfigValuePtr("master:special_scale_factor")->floatValue;
|
|
|
|
PWINDOW->m_vRealPosition = calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f;
|
|
PWINDOW->m_vRealSize = calcSize * *PSCALEFACTOR;
|
|
|
|
g_pXWaylandManager->setWindowSize(PWINDOW, calcSize * *PSCALEFACTOR);
|
|
} else {
|
|
PWINDOW->m_vRealSize = calcSize;
|
|
PWINDOW->m_vRealPosition = calcPos;
|
|
|
|
g_pXWaylandManager->setWindowSize(PWINDOW, calcSize);
|
|
}
|
|
|
|
if (m_bForceWarps) {
|
|
g_pHyprRenderer->damageWindow(PWINDOW);
|
|
|
|
PWINDOW->m_vRealPosition.warp();
|
|
PWINDOW->m_vRealSize.warp();
|
|
|
|
g_pHyprRenderer->damageWindow(PWINDOW);
|
|
}
|
|
|
|
PWINDOW->updateWindowDecos();
|
|
}
|
|
|
|
bool CHyprMasterLayout::isWindowTiled(CWindow* pWindow) {
|
|
return getNodeFromWindow(pWindow) != nullptr;
|
|
}
|
|
|
|
void CHyprMasterLayout::resizeActiveWindow(const Vector2D& pixResize, CWindow* pWindow) {
|
|
const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow;
|
|
|
|
if (!g_pCompositor->windowValidMapped(PWINDOW))
|
|
return;
|
|
|
|
const auto PNODE = getNodeFromWindow(PWINDOW);
|
|
|
|
if (!PNODE) {
|
|
PWINDOW->m_vRealSize = Vector2D(std::max((PWINDOW->m_vRealSize.goalv() + pixResize).x, 20.0), std::max((PWINDOW->m_vRealSize.goalv() + pixResize).y, 20.0));
|
|
PWINDOW->updateWindowDecos();
|
|
return;
|
|
}
|
|
|
|
// get monitor
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
|
|
|
|
if (getNodesOnWorkspace(PWINDOW->m_iWorkspaceID) < 2)
|
|
return;
|
|
|
|
m_bForceWarps = true;
|
|
|
|
const auto PWORKSPACEDATA = getMasterWorkspaceData(PMONITOR->activeWorkspace);
|
|
|
|
double delta = 0;
|
|
|
|
switch (PWORKSPACEDATA->orientation) {
|
|
case ORIENTATION_LEFT: delta = pixResize.x / PMONITOR->vecSize.x; break;
|
|
case ORIENTATION_RIGHT: delta = -pixResize.x / PMONITOR->vecSize.x; break;
|
|
case ORIENTATION_BOTTOM: delta = -pixResize.y / PMONITOR->vecSize.y; break;
|
|
case ORIENTATION_TOP: delta = pixResize.y / PMONITOR->vecSize.y; break;
|
|
case ORIENTATION_CENTER: delta = pixResize.x / PMONITOR->vecSize.x; break;
|
|
default: UNREACHABLE();
|
|
}
|
|
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.isMaster && n.workspaceID == PMONITOR->activeWorkspace)
|
|
n.percMaster = std::clamp(n.percMaster + delta, 0.05, 0.95);
|
|
}
|
|
|
|
// check the up/down resize
|
|
const auto RESIZEDELTA = PWORKSPACEDATA->orientation % 2 == 1 ? pixResize.x : pixResize.y;
|
|
|
|
if (RESIZEDELTA != 0) {
|
|
if (PNODE->isMaster && getMastersOnWorkspace(PNODE->workspaceID) > 1) {
|
|
// check master size
|
|
const auto SIZE = PWORKSPACEDATA->orientation % 2 == 1 ?
|
|
(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) / getMastersOnWorkspace(PNODE->workspaceID) :
|
|
(PMONITOR->vecSize.y - PMONITOR->vecReservedTopLeft.y - PMONITOR->vecReservedBottomRight.y) / getMastersOnWorkspace(PNODE->workspaceID);
|
|
PNODE->percSize = std::clamp(PNODE->percSize + RESIZEDELTA / SIZE, 0.05, 1.95);
|
|
} else if (!PNODE->isMaster && (getNodesOnWorkspace(PWINDOW->m_iWorkspaceID) - getMastersOnWorkspace(PNODE->workspaceID)) > 1) {
|
|
const auto SIZE = PWORKSPACEDATA->orientation % 2 == 1 ?
|
|
(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) / getNodesOnWorkspace(PNODE->workspaceID) :
|
|
(PMONITOR->vecSize.y - PMONITOR->vecReservedTopLeft.y - PMONITOR->vecReservedBottomRight.y) / getNodesOnWorkspace(PNODE->workspaceID);
|
|
PNODE->percSize = std::clamp(PNODE->percSize + RESIZEDELTA / SIZE, 0.05, 1.95);
|
|
}
|
|
}
|
|
|
|
recalculateMonitor(PMONITOR->ID);
|
|
|
|
m_bForceWarps = false;
|
|
}
|
|
|
|
void CHyprMasterLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode fullscreenMode, bool on) {
|
|
if (!g_pCompositor->windowValidMapped(pWindow))
|
|
return;
|
|
|
|
if (on == pWindow->m_bIsFullscreen || g_pCompositor->isWorkspaceSpecial(pWindow->m_iWorkspaceID))
|
|
return; // ignore
|
|
|
|
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
|
|
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
|
|
|
|
if (PWORKSPACE->m_bHasFullscreenWindow && on) {
|
|
// if the window wants to be fullscreen but there already is one,
|
|
// ignore the request.
|
|
return;
|
|
}
|
|
|
|
// otherwise, accept it.
|
|
pWindow->m_bIsFullscreen = on;
|
|
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
|
|
|
|
g_pEventManager->postEvent(SHyprIPCEvent{"fullscreen", std::to_string((int)on)});
|
|
EMIT_HOOK_EVENT("fullscreen", pWindow);
|
|
|
|
if (!pWindow->m_bIsFullscreen) {
|
|
// if it got its fullscreen disabled, set back its node if it had one
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
if (PNODE)
|
|
applyNodeDataToWindow(PNODE);
|
|
else {
|
|
// get back its' dimensions from position and size
|
|
pWindow->m_vRealPosition = pWindow->m_vLastFloatingPosition;
|
|
pWindow->m_vRealSize = pWindow->m_vLastFloatingSize;
|
|
|
|
pWindow->m_sSpecialRenderData.rounding = true;
|
|
pWindow->m_sSpecialRenderData.border = true;
|
|
pWindow->m_sSpecialRenderData.decorate = true;
|
|
}
|
|
} else {
|
|
// if it now got fullscreen, make it fullscreen
|
|
|
|
PWORKSPACE->m_efFullscreenMode = fullscreenMode;
|
|
|
|
// save position and size if floating
|
|
if (pWindow->m_bIsFloating) {
|
|
pWindow->m_vLastFloatingSize = pWindow->m_vRealSize.goalv();
|
|
pWindow->m_vLastFloatingPosition = pWindow->m_vRealPosition.goalv();
|
|
pWindow->m_vPosition = pWindow->m_vRealPosition.goalv();
|
|
pWindow->m_vSize = pWindow->m_vRealSize.goalv();
|
|
}
|
|
|
|
// apply new pos and size being monitors' box
|
|
if (fullscreenMode == FULLSCREEN_FULL) {
|
|
pWindow->m_vRealPosition = PMONITOR->vecPosition;
|
|
pWindow->m_vRealSize = PMONITOR->vecSize;
|
|
} else {
|
|
// This is a massive hack.
|
|
// We make a fake "only" node and apply
|
|
// To keep consistent with the settings without C+P code
|
|
|
|
SMasterNodeData fakeNode;
|
|
fakeNode.pWindow = pWindow;
|
|
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
|
|
fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight;
|
|
fakeNode.workspaceID = pWindow->m_iWorkspaceID;
|
|
pWindow->m_vPosition = fakeNode.position;
|
|
pWindow->m_vSize = fakeNode.size;
|
|
|
|
applyNodeDataToWindow(&fakeNode);
|
|
}
|
|
}
|
|
|
|
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
|
|
|
|
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goalv());
|
|
|
|
g_pCompositor->moveWindowToTop(pWindow);
|
|
|
|
recalculateMonitor(PMONITOR->ID);
|
|
}
|
|
|
|
void CHyprMasterLayout::recalculateWindow(CWindow* pWindow) {
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
|
|
if (!PNODE)
|
|
return;
|
|
|
|
recalculateMonitor(pWindow->m_iMonitorID);
|
|
}
|
|
|
|
SWindowRenderLayoutHints CHyprMasterLayout::requestRenderHints(CWindow* pWindow) {
|
|
// window should be valid, insallah
|
|
|
|
SWindowRenderLayoutHints hints;
|
|
|
|
return hints; // master doesnt have any hints
|
|
}
|
|
|
|
void CHyprMasterLayout::switchWindows(CWindow* pWindow, CWindow* pWindow2) {
|
|
// windows should be valid, insallah
|
|
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
const auto PNODE2 = getNodeFromWindow(pWindow2);
|
|
|
|
if (!PNODE2 || !PNODE)
|
|
return;
|
|
|
|
const auto inheritFullscreen = prepareLoseFocus(pWindow);
|
|
|
|
if (PNODE->workspaceID != PNODE2->workspaceID) {
|
|
std::swap(pWindow2->m_iMonitorID, pWindow->m_iMonitorID);
|
|
std::swap(pWindow2->m_iWorkspaceID, pWindow->m_iWorkspaceID);
|
|
}
|
|
|
|
// massive hack: just swap window pointers, lol
|
|
PNODE->pWindow = pWindow2;
|
|
PNODE2->pWindow = pWindow;
|
|
|
|
recalculateMonitor(pWindow->m_iMonitorID);
|
|
if (PNODE2->workspaceID != PNODE->workspaceID)
|
|
recalculateMonitor(pWindow2->m_iMonitorID);
|
|
|
|
g_pHyprRenderer->damageWindow(pWindow);
|
|
g_pHyprRenderer->damageWindow(pWindow2);
|
|
|
|
prepareNewFocus(pWindow2, inheritFullscreen);
|
|
}
|
|
|
|
void CHyprMasterLayout::alterSplitRatio(CWindow* pWindow, float ratio, bool exact) {
|
|
// window should be valid, insallah
|
|
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
|
|
if (!PNODE)
|
|
return;
|
|
|
|
const auto PMASTER = getMasterNodeOnWorkspace(pWindow->m_iWorkspaceID);
|
|
|
|
float newRatio = exact ? ratio : PMASTER->percMaster + ratio;
|
|
PMASTER->percMaster = std::clamp(newRatio, 0.05f, 0.95f);
|
|
|
|
recalculateMonitor(pWindow->m_iMonitorID);
|
|
}
|
|
|
|
CWindow* CHyprMasterLayout::getNextWindow(CWindow* pWindow, bool next) {
|
|
if (!isWindowTiled(pWindow))
|
|
return nullptr;
|
|
|
|
const auto PNODE = getNodeFromWindow(pWindow);
|
|
|
|
auto nodes = m_lMasterNodesData;
|
|
if (!next)
|
|
std::reverse(nodes.begin(), nodes.end());
|
|
|
|
const auto NODEIT = std::find(nodes.begin(), nodes.end(), *PNODE);
|
|
|
|
const bool ISMASTER = PNODE->isMaster;
|
|
|
|
auto CANDIDATE = std::find_if(NODEIT, nodes.end(), [&](const auto& other) { return other != *PNODE && ISMASTER == other.isMaster && other.workspaceID == PNODE->workspaceID; });
|
|
if (CANDIDATE == nodes.end())
|
|
CANDIDATE =
|
|
std::find_if(nodes.begin(), nodes.end(), [&](const auto& other) { return other != *PNODE && ISMASTER != other.isMaster && other.workspaceID == PNODE->workspaceID; });
|
|
|
|
return CANDIDATE == nodes.end() ? nullptr : CANDIDATE->pWindow;
|
|
}
|
|
|
|
bool CHyprMasterLayout::prepareLoseFocus(CWindow* pWindow) {
|
|
if (!pWindow)
|
|
return false;
|
|
|
|
//if the current window is fullscreen, make it normal again if we are about to lose focus
|
|
if (pWindow->m_bIsFullscreen) {
|
|
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
|
|
static auto* const INHERIT = &g_pConfigManager->getConfigValuePtr("master:inherit_fullscreen")->intValue;
|
|
return *INHERIT == 1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CHyprMasterLayout::prepareNewFocus(CWindow* pWindow, bool inheritFullscreen) {
|
|
if (!pWindow)
|
|
return;
|
|
|
|
if (inheritFullscreen)
|
|
g_pCompositor->setWindowFullscreen(pWindow, true, g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID)->m_efFullscreenMode);
|
|
}
|
|
|
|
std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::string message) {
|
|
auto switchToWindow = [&](CWindow* PWINDOWTOCHANGETO) {
|
|
if (!g_pCompositor->windowValidMapped(PWINDOWTOCHANGETO))
|
|
return;
|
|
|
|
g_pCompositor->focusWindow(PWINDOWTOCHANGETO);
|
|
g_pCompositor->warpCursorTo(PWINDOWTOCHANGETO->m_vRealPosition.goalv() + PWINDOWTOCHANGETO->m_vRealSize.goalv() / 2.f);
|
|
};
|
|
|
|
CVarList vars(message, 0, ' ');
|
|
|
|
if (vars.size() < 1 || vars[0].empty()) {
|
|
Debug::log(ERR, "layoutmsg called without params");
|
|
return 0;
|
|
}
|
|
|
|
auto command = vars[0];
|
|
|
|
// swapwithmaster <master | child | auto>
|
|
// first message argument can have the following values:
|
|
// * master - keep the focus at the new master
|
|
// * child - keep the focus at the new child
|
|
// * auto (default) - swap the focus (keep the focus of the previously selected window)
|
|
if (command == "swapwithmaster") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
if (!isWindowTiled(PWINDOW))
|
|
return 0;
|
|
|
|
const auto PMASTER = getMasterNodeOnWorkspace(PWINDOW->m_iWorkspaceID);
|
|
|
|
if (!PMASTER)
|
|
return 0;
|
|
|
|
const auto NEWCHILD = PMASTER->pWindow;
|
|
|
|
if (PMASTER->pWindow != PWINDOW) {
|
|
const auto NEWMASTER = PWINDOW;
|
|
const bool newFocusToChild = vars.size() >= 2 && vars[1] == "child";
|
|
const bool inheritFullscreen = prepareLoseFocus(NEWMASTER);
|
|
switchWindows(NEWMASTER, NEWCHILD);
|
|
const auto NEWFOCUS = newFocusToChild ? NEWCHILD : NEWMASTER;
|
|
switchToWindow(NEWFOCUS);
|
|
prepareNewFocus(NEWFOCUS, inheritFullscreen);
|
|
} else {
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == PMASTER->workspaceID && !n.isMaster) {
|
|
const auto NEWMASTER = n.pWindow;
|
|
const bool inheritFullscreen = prepareLoseFocus(NEWCHILD);
|
|
switchWindows(NEWMASTER, NEWCHILD);
|
|
const bool newFocusToMaster = vars.size() >= 2 && vars[1] == "master";
|
|
const auto NEWFOCUS = newFocusToMaster ? NEWMASTER : NEWCHILD;
|
|
switchToWindow(NEWFOCUS);
|
|
prepareNewFocus(NEWFOCUS, inheritFullscreen);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
// focusmaster <master | auto>
|
|
// first message argument can have the following values:
|
|
// * master - keep the focus at the new master, even if it was focused before
|
|
// * auto (default) - swap the focus with the first child, if the current focus was master, otherwise focus master
|
|
else if (command == "focusmaster") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
const bool inheritFullscreen = prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PMASTER = getMasterNodeOnWorkspace(PWINDOW->m_iWorkspaceID);
|
|
|
|
if (!PMASTER)
|
|
return 0;
|
|
|
|
if (PMASTER->pWindow != PWINDOW) {
|
|
switchToWindow(PMASTER->pWindow);
|
|
prepareNewFocus(PMASTER->pWindow, inheritFullscreen);
|
|
} else if (vars.size() >= 2 && vars[1] == "master") {
|
|
return 0;
|
|
} else {
|
|
// if master is focused keep master focused (don't do anything)
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == PMASTER->workspaceID && !n.isMaster) {
|
|
switchToWindow(n.pWindow);
|
|
prepareNewFocus(n.pWindow, inheritFullscreen);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} else if (command == "cyclenext") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
const bool inheritFullscreen = prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PNEXTWINDOW = getNextWindow(PWINDOW, true);
|
|
switchToWindow(PNEXTWINDOW);
|
|
prepareNewFocus(PNEXTWINDOW, inheritFullscreen);
|
|
} else if (command == "cycleprev") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
const bool inheritFullscreen = prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PPREVWINDOW = getNextWindow(PWINDOW, false);
|
|
switchToWindow(PPREVWINDOW);
|
|
prepareNewFocus(PPREVWINDOW, inheritFullscreen);
|
|
} else if (command == "swapnext") {
|
|
if (!g_pCompositor->windowValidMapped(header.pWindow))
|
|
return 0;
|
|
|
|
if (header.pWindow->m_bIsFloating) {
|
|
g_pKeybindManager->m_mDispatchers["swapnext"]("");
|
|
return 0;
|
|
}
|
|
|
|
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, true);
|
|
|
|
if (PWINDOWTOSWAPWITH) {
|
|
prepareLoseFocus(header.pWindow);
|
|
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
|
|
g_pCompositor->focusWindow(header.pWindow);
|
|
}
|
|
} else if (command == "swapprev") {
|
|
if (!g_pCompositor->windowValidMapped(header.pWindow))
|
|
return 0;
|
|
|
|
if (header.pWindow->m_bIsFloating) {
|
|
g_pKeybindManager->m_mDispatchers["swapnext"]("prev");
|
|
return 0;
|
|
}
|
|
|
|
const auto PWINDOWTOSWAPWITH = getNextWindow(header.pWindow, false);
|
|
|
|
if (PWINDOWTOSWAPWITH) {
|
|
prepareLoseFocus(header.pWindow);
|
|
switchWindows(header.pWindow, PWINDOWTOSWAPWITH);
|
|
g_pCompositor->focusWindow(header.pWindow);
|
|
}
|
|
} else if (command == "addmaster") {
|
|
if (!g_pCompositor->windowValidMapped(header.pWindow))
|
|
return 0;
|
|
|
|
if (header.pWindow->m_bIsFloating)
|
|
return 0;
|
|
|
|
const auto PNODE = getNodeFromWindow(header.pWindow);
|
|
|
|
const auto WINDOWS = getNodesOnWorkspace(header.pWindow->m_iWorkspaceID);
|
|
const auto MASTERS = getMastersOnWorkspace(header.pWindow->m_iWorkspaceID);
|
|
static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue;
|
|
|
|
if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0)
|
|
return 0;
|
|
prepareLoseFocus(header.pWindow);
|
|
|
|
if (!PNODE || PNODE->isMaster) {
|
|
// first non-master node
|
|
for (auto& n : m_lMasterNodesData) {
|
|
if (n.workspaceID == header.pWindow->m_iWorkspaceID && !n.isMaster) {
|
|
n.isMaster = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
PNODE->isMaster = true;
|
|
}
|
|
|
|
recalculateMonitor(header.pWindow->m_iMonitorID);
|
|
|
|
} else if (command == "removemaster") {
|
|
|
|
if (!g_pCompositor->windowValidMapped(header.pWindow))
|
|
return 0;
|
|
|
|
if (header.pWindow->m_bIsFloating)
|
|
return 0;
|
|
|
|
const auto PNODE = getNodeFromWindow(header.pWindow);
|
|
|
|
const auto WINDOWS = getNodesOnWorkspace(header.pWindow->m_iWorkspaceID);
|
|
const auto MASTERS = getMastersOnWorkspace(header.pWindow->m_iWorkspaceID);
|
|
|
|
if (WINDOWS < 2 || MASTERS < 2)
|
|
return 0;
|
|
|
|
prepareLoseFocus(header.pWindow);
|
|
|
|
if (!PNODE || !PNODE->isMaster) {
|
|
// first non-master node
|
|
for (auto& nd : m_lMasterNodesData | std::views::reverse) {
|
|
if (nd.workspaceID == header.pWindow->m_iWorkspaceID && nd.isMaster) {
|
|
nd.isMaster = false;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
PNODE->isMaster = false;
|
|
}
|
|
|
|
recalculateMonitor(header.pWindow->m_iMonitorID);
|
|
} else if (command == "orientationleft" || command == "orientationright" || command == "orientationtop" || command == "orientationbottom" || command == "orientationcenter") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->m_iWorkspaceID);
|
|
|
|
if (command == "orientationleft")
|
|
PWORKSPACEDATA->orientation = ORIENTATION_LEFT;
|
|
else if (command == "orientationright")
|
|
PWORKSPACEDATA->orientation = ORIENTATION_RIGHT;
|
|
else if (command == "orientationtop")
|
|
PWORKSPACEDATA->orientation = ORIENTATION_TOP;
|
|
else if (command == "orientationbottom")
|
|
PWORKSPACEDATA->orientation = ORIENTATION_BOTTOM;
|
|
else if (command == "orientationcenter")
|
|
PWORKSPACEDATA->orientation = ORIENTATION_CENTER;
|
|
|
|
recalculateMonitor(header.pWindow->m_iMonitorID);
|
|
|
|
} else if (command == "orientationnext") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->m_iWorkspaceID);
|
|
|
|
if (PWORKSPACEDATA->orientation == ORIENTATION_CENTER) {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_LEFT;
|
|
} else {
|
|
PWORKSPACEDATA->orientation = (eOrientation)(PWORKSPACEDATA->orientation + 1);
|
|
}
|
|
|
|
recalculateMonitor(header.pWindow->m_iMonitorID);
|
|
} else if (command == "orientationprev") {
|
|
const auto PWINDOW = header.pWindow;
|
|
|
|
if (!PWINDOW)
|
|
return 0;
|
|
|
|
prepareLoseFocus(PWINDOW);
|
|
|
|
const auto PWORKSPACEDATA = getMasterWorkspaceData(PWINDOW->m_iWorkspaceID);
|
|
|
|
if (PWORKSPACEDATA->orientation == ORIENTATION_LEFT) {
|
|
PWORKSPACEDATA->orientation = ORIENTATION_CENTER;
|
|
} else {
|
|
PWORKSPACEDATA->orientation = (eOrientation)(PWORKSPACEDATA->orientation - 1);
|
|
}
|
|
|
|
recalculateMonitor(header.pWindow->m_iMonitorID);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CHyprMasterLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
|
|
const auto PNODE = getNodeFromWindow(from);
|
|
|
|
if (!PNODE)
|
|
return;
|
|
|
|
PNODE->pWindow = to;
|
|
|
|
applyNodeDataToWindow(PNODE);
|
|
}
|
|
|
|
void CHyprMasterLayout::onEnable() {
|
|
for (auto& w : g_pCompositor->m_vWindows) {
|
|
if (w->m_bIsFloating || !w->m_bMappedX11 || !w->m_bIsMapped || w->isHidden())
|
|
continue;
|
|
|
|
onWindowCreatedTiling(w.get());
|
|
}
|
|
}
|
|
|
|
void CHyprMasterLayout::onDisable() {
|
|
m_lMasterNodesData.clear();
|
|
}
|