layout: rethonk layouts from the ground up (#12890)
Rewrites layouts to be much smaller, and deal with much less annoying BS. Improves the overall architecture, unifies handling of pseudotiling, and various other improvements.
This commit is contained in:
parent
51f8849e54
commit
723870337f
82 changed files with 8431 additions and 5527 deletions
|
|
@ -110,6 +110,7 @@ add_compile_options(
|
|||
-Wno-narrowing
|
||||
-Wno-pointer-arith
|
||||
-Wno-clobbered
|
||||
-frtti
|
||||
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
|
||||
|
||||
# disable lto as it may break plugins
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#define private public
|
||||
#include <src/config/ConfigManager.hpp>
|
||||
#include <src/config/ConfigDescriptions.hpp>
|
||||
#include <src/layout/IHyprLayout.hpp>
|
||||
#include <src/managers/LayoutManager.hpp>
|
||||
#include <src/managers/input/InputManager.hpp>
|
||||
#include <src/managers/PointerManager.hpp>
|
||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||
|
|
@ -15,6 +13,7 @@
|
|||
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||
#include <src/Compositor.hpp>
|
||||
#include <src/desktop/state/FocusState.hpp>
|
||||
#include <src/layout/LayoutManager.hpp>
|
||||
#undef private
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
|
@ -53,8 +52,9 @@ static SDispatchResult snapMove(std::string in) {
|
|||
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
|
||||
Vector2D size = PLASTWINDOW->m_realSize->goal();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->performSnap(pos, size, PLASTWINDOW, MBIND_MOVE, -1, size);
|
||||
*PLASTWINDOW->m_realPosition = pos.round();
|
||||
g_layoutManager->performSnap(pos, size, PLASTWINDOW->layoutTarget(), MBIND_MOVE, -1, size);
|
||||
|
||||
PLASTWINDOW->layoutTarget()->setPositionGlobal(CBox{pos, size});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "desktop/state/FocusState.hpp"
|
||||
#include "desktop/history/WindowHistoryTracker.hpp"
|
||||
#include "desktop/history/WorkspaceHistoryTracker.hpp"
|
||||
#include "desktop/view/Group.hpp"
|
||||
#include "helpers/Splashes.hpp"
|
||||
#include "config/ConfigValue.hpp"
|
||||
#include "config/ConfigWatcher.hpp"
|
||||
|
|
@ -62,7 +63,6 @@
|
|||
#include "managers/EventManager.hpp"
|
||||
#include "managers/HookSystemManager.hpp"
|
||||
#include "managers/ProtocolManager.hpp"
|
||||
#include "managers/LayoutManager.hpp"
|
||||
#include "managers/WelcomeManager.hpp"
|
||||
#include "render/AsyncResourceGatherer.hpp"
|
||||
#include "plugins/PluginSystem.hpp"
|
||||
|
|
@ -71,6 +71,8 @@
|
|||
#include "debug/HyprDebugOverlay.hpp"
|
||||
#include "helpers/MonitorFrameScheduler.hpp"
|
||||
#include "i18n/Engine.hpp"
|
||||
#include "layout/LayoutManager.hpp"
|
||||
#include "layout/target/WindowTarget.hpp"
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <aquamarine/input/Input.hpp>
|
||||
|
|
@ -590,7 +592,7 @@ void CCompositor::cleanup() {
|
|||
g_pHyprRenderer.reset();
|
||||
g_pHyprOpenGL.reset();
|
||||
g_pConfigManager.reset();
|
||||
g_pLayoutManager.reset();
|
||||
g_layoutManager.reset();
|
||||
g_pHyprError.reset();
|
||||
g_pConfigManager.reset();
|
||||
g_pKeybindManager.reset();
|
||||
|
|
@ -642,7 +644,7 @@ void CCompositor::initManagers(eManagersInitStage stage) {
|
|||
g_pHyprError = makeUnique<CHyprError>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the LayoutManager!");
|
||||
g_pLayoutManager = makeUnique<CLayoutManager>();
|
||||
g_layoutManager = makeUnique<Layout::CLayoutManager>();
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Creating the TokenManager!");
|
||||
g_pTokenManager = makeUnique<CTokenManager>();
|
||||
|
|
@ -1377,8 +1379,8 @@ void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) {
|
|||
m_windowsFadingOut.emplace_back(pWindow);
|
||||
}
|
||||
|
||||
PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
||||
if (!isDirection(dir))
|
||||
PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection dir) {
|
||||
if (dir == Math::DIRECTION_DEFAULT)
|
||||
return nullptr;
|
||||
|
||||
const auto PMONITOR = pWindow->m_monitor.lock();
|
||||
|
|
@ -1395,8 +1397,8 @@ PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, char dir) {
|
|||
return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating);
|
||||
}
|
||||
|
||||
PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow, bool useVectorAngles) {
|
||||
if (!isDirection(dir))
|
||||
PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow, bool useVectorAngles) {
|
||||
if (dir == Math::DIRECTION_DEFAULT)
|
||||
return nullptr;
|
||||
|
||||
// 0 -> history, 1 -> shared length
|
||||
|
|
@ -1431,28 +1433,27 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
|||
double intersectLength = -1;
|
||||
|
||||
switch (dir) {
|
||||
case 'l':
|
||||
case Math::DIRECTION_LEFT:
|
||||
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
case Math::DIRECTION_RIGHT:
|
||||
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
|
||||
intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
case 'u':
|
||||
case Math::DIRECTION_UP:
|
||||
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
case 'd':
|
||||
case Math::DIRECTION_DOWN:
|
||||
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
|
||||
intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (*PMETHOD == 0 /* history */) {
|
||||
|
|
@ -1481,12 +1482,8 @@ PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorks
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (dir == 'u')
|
||||
dir = 't';
|
||||
if (dir == 'd')
|
||||
dir = 'b';
|
||||
|
||||
static const std::unordered_map<char, Vector2D> VECTORS = {{'r', {1, 0}}, {'t', {0, -1}}, {'b', {0, 1}}, {'l', {-1, 0}}};
|
||||
static const std::unordered_map<Math::eDirection, Vector2D> VECTORS = {
|
||||
{Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}};
|
||||
|
||||
//
|
||||
auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double {
|
||||
|
|
@ -1665,11 +1662,11 @@ CBox CCompositor::calculateX11WorkArea() {
|
|||
return workbox;
|
||||
}
|
||||
|
||||
PHLMONITOR CCompositor::getMonitorInDirection(const char& dir) {
|
||||
PHLMONITOR CCompositor::getMonitorInDirection(Math::eDirection dir) {
|
||||
return getMonitorInDirection(Desktop::focusState()->monitor(), dir);
|
||||
}
|
||||
|
||||
PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const char& dir) {
|
||||
PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::eDirection dir) {
|
||||
if (!pSourceMonitor)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -1686,7 +1683,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const c
|
|||
const auto POSB = m->m_position;
|
||||
const auto SIZEB = m->m_size;
|
||||
switch (dir) {
|
||||
case 'l':
|
||||
case Math::DIRECTION_LEFT:
|
||||
if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
|
||||
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
if (INTERSECTLEN > longestIntersect) {
|
||||
|
|
@ -1695,7 +1692,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const c
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
case Math::DIRECTION_RIGHT:
|
||||
if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
|
||||
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
|
||||
if (INTERSECTLEN > longestIntersect) {
|
||||
|
|
@ -1704,8 +1701,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const c
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
case 'u':
|
||||
case Math::DIRECTION_UP:
|
||||
if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
|
||||
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
if (INTERSECTLEN > longestIntersect) {
|
||||
|
|
@ -1714,8 +1710,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const c
|
|||
}
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
case 'd':
|
||||
case Math::DIRECTION_DOWN:
|
||||
if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
|
||||
const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
|
||||
if (INTERSECTLEN > longestIntersect) {
|
||||
|
|
@ -1724,6 +1719,7 @@ PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, const c
|
|||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1779,7 +1775,7 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
|
|||
|
||||
// additionally, move floating and fs windows manually
|
||||
if (w->m_isFloating)
|
||||
*w->m_realPosition = w->m_realPosition->goal() - pMonitorA->m_position + pMonitorB->m_position;
|
||||
w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorA->m_position + pMonitorB->m_position));
|
||||
|
||||
if (w->isFullscreen()) {
|
||||
*w->m_realPosition = pMonitorB->m_position;
|
||||
|
|
@ -1804,7 +1800,7 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
|
|||
|
||||
// additionally, move floating and fs windows manually
|
||||
if (w->m_isFloating)
|
||||
*w->m_realPosition = w->m_realPosition->goal() - pMonitorB->m_position + pMonitorA->m_position;
|
||||
w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorB->m_position + pMonitorA->m_position));
|
||||
|
||||
if (w->isFullscreen()) {
|
||||
*w->m_realPosition = pMonitorA->m_position;
|
||||
|
|
@ -1818,8 +1814,8 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
|
|||
pMonitorA->m_activeWorkspace = PWORKSPACEB;
|
||||
pMonitorB->m_activeWorkspace = PWORKSPACEA;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorA->m_id);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorB->m_id);
|
||||
g_layoutManager->recalculateMonitor(pMonitorA);
|
||||
g_layoutManager->recalculateMonitor(pMonitorB);
|
||||
|
||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(
|
||||
PWORKSPACEB, PWORKSPACEB->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
|
||||
|
|
@ -1831,7 +1827,8 @@ void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitor
|
|||
Desktop::focusState()->fullWindowFocus(
|
||||
LASTWIN ? LASTWIN :
|
||||
(g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(),
|
||||
Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING)));
|
||||
Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING)),
|
||||
Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
|
||||
const auto PNEWWORKSPACE = pMonitorA->m_id == Desktop::focusState()->monitor()->m_id ? PWORKSPACEB : PWORKSPACEA;
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "workspace", .data = PNEWWORKSPACE->m_name});
|
||||
|
|
@ -1853,7 +1850,7 @@ PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) {
|
|||
if (name == "current")
|
||||
return Desktop::focusState()->monitor();
|
||||
else if (isDirection(name))
|
||||
return getMonitorInDirection(name[0]);
|
||||
return getMonitorInDirection(Math::fromChar(name[0]));
|
||||
else if (name[0] == '+' || name[0] == '-') {
|
||||
// relative
|
||||
|
||||
|
|
@ -1989,17 +1986,18 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
|
|||
if (w->m_isMapped && !w->isHidden()) {
|
||||
if (POLDMON) {
|
||||
if (w->m_isFloating)
|
||||
*w->m_realPosition = w->m_realPosition->goal() - POLDMON->m_position + pMonitor->m_position;
|
||||
w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-POLDMON->m_position + pMonitor->m_position));
|
||||
|
||||
if (w->isFullscreen()) {
|
||||
*w->m_realPosition = pMonitor->m_position;
|
||||
*w->m_realSize = pMonitor->m_size;
|
||||
}
|
||||
} else
|
||||
*w->m_realPosition = Vector2D{
|
||||
(pMonitor->m_size.x != 0) ? sc<int>(w->m_realPosition->goal().x) % sc<int>(pMonitor->m_size.x) : 0,
|
||||
(pMonitor->m_size.y != 0) ? sc<int>(w->m_realPosition->goal().y) % sc<int>(pMonitor->m_size.y) : 0,
|
||||
};
|
||||
w->layoutTarget()->setPositionGlobal(CBox{Vector2D{
|
||||
(pMonitor->m_size.x != 0) ? sc<int>(w->m_realPosition->goal().x) % sc<int>(pMonitor->m_size.x) : 0,
|
||||
(pMonitor->m_size.y != 0) ? sc<int>(w->m_realPosition->goal().y) % sc<int>(pMonitor->m_size.y) : 0,
|
||||
},
|
||||
w->layoutTarget()->position().size()});
|
||||
}
|
||||
|
||||
w->updateToplevel();
|
||||
|
|
@ -2027,7 +2025,7 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
|
|||
|
||||
pWorkspace->m_events.activeChanged.emit();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->m_id);
|
||||
g_layoutManager->recalculateMonitor(pMonitor);
|
||||
|
||||
g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
||||
pWorkspace->m_visible = true;
|
||||
|
|
@ -2040,7 +2038,7 @@ void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMo
|
|||
|
||||
// finalize
|
||||
if (POLDMON) {
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->m_id);
|
||||
g_layoutManager->recalculateMonitor(POLDMON);
|
||||
if (valid(POLDMON->m_activeWorkspace))
|
||||
g_pDesktopAnimationManager->setFullscreenFadeAnimation(POLDMON->m_activeWorkspace,
|
||||
POLDMON->m_activeWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN :
|
||||
|
|
@ -2138,15 +2136,16 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
|
||||
Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
PWINDOW->updateDecorationValues();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
|
||||
g_layoutManager->recalculateMonitor(PMONITOR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(PWINDOW, CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE);
|
||||
PWORKSPACE->m_fullscreenMode = EFFECTIVE_MODE;
|
||||
PWORKSPACE->m_hasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE;
|
||||
|
||||
g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), CURRENT_EFFECTIVE_MODE, EFFECTIVE_MODE);
|
||||
|
||||
PWINDOW->m_fullscreenState.internal = state.internal;
|
||||
PWORKSPACE->m_fullscreenMode = EFFECTIVE_MODE;
|
||||
PWORKSPACE->m_hasFullscreenWindow = EFFECTIVE_MODE != FSMODE_NONE;
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(EFFECTIVE_MODE) != FSMODE_NONE)});
|
||||
EMIT_HOOK_EVENT("fullscreen", PWINDOW);
|
||||
|
|
@ -2155,7 +2154,7 @@ void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::Vie
|
|||
Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
|
||||
PWINDOW->updateDecorationValues();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
|
||||
g_layoutManager->recalculateMonitor(PMONITOR);
|
||||
|
||||
// make all windows and layers on the same workspace under the fullscreen window
|
||||
for (auto const& w : m_windows) {
|
||||
|
|
@ -2268,7 +2267,7 @@ PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) {
|
|||
}
|
||||
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (!w->m_isMapped || (w->isHidden() && !g_pLayoutManager->getCurrentLayout()->isWindowReachable(w)))
|
||||
if (!w->m_isMapped)
|
||||
continue;
|
||||
|
||||
switch (mode) {
|
||||
|
|
@ -2573,59 +2572,30 @@ void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWor
|
|||
setWindowFullscreenInternal(pWindow, FSMODE_NONE);
|
||||
|
||||
const PHLWINDOW pFirstWindowOnWorkspace = pWorkspace->getFirstWindow();
|
||||
const int visibleWindowsOnWorkspace = pWorkspace->getWindows(std::nullopt, true);
|
||||
const int visibleWindowsOnWorkspace = pWorkspace->getWindows(true, std::nullopt, true);
|
||||
const auto POSTOMON = pWindow->m_realPosition->goal() - (pWindow->m_monitor ? pWindow->m_monitor->m_position : Vector2D{});
|
||||
const auto PWORKSPACEMONITOR = pWorkspace->m_monitor.lock();
|
||||
|
||||
if (!pWindow->m_isFloating)
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow);
|
||||
|
||||
pWindow->moveToWorkspace(pWorkspace);
|
||||
pWindow->m_monitor = pWorkspace->m_monitor;
|
||||
|
||||
static auto PGROUPONMOVETOWORKSPACE = CConfigValue<Hyprlang::INT>("group:group_on_movetoworkspace");
|
||||
if (*PGROUPONMOVETOWORKSPACE && visibleWindowsOnWorkspace == 1 && pFirstWindowOnWorkspace && pFirstWindowOnWorkspace != pWindow &&
|
||||
pFirstWindowOnWorkspace->m_groupData.pNextWindow.lock() && pWindow->canBeGroupedInto(pFirstWindowOnWorkspace)) {
|
||||
|
||||
pWindow->m_isFloating = pFirstWindowOnWorkspace->m_isFloating; // match the floating state. Needed to group tiled into floated and vice versa.
|
||||
if (!pWindow->m_groupData.pNextWindow.expired()) {
|
||||
PHLWINDOW next = pWindow->m_groupData.pNextWindow.lock();
|
||||
while (next != pWindow) {
|
||||
next->m_isFloating = pFirstWindowOnWorkspace->m_isFloating; // match the floating state of group members
|
||||
next = next->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
}
|
||||
|
||||
static auto USECURRPOS = CConfigValue<Hyprlang::INT>("group:insert_after_current");
|
||||
(*USECURRPOS ? pFirstWindowOnWorkspace : pFirstWindowOnWorkspace->getGroupTail())->insertWindowToGroup(pWindow);
|
||||
|
||||
pFirstWindowOnWorkspace->setGroupCurrent(pWindow);
|
||||
pWindow->updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
|
||||
|
||||
if (!pWindow->getDecorationByType(DECORATION_GROUPBAR))
|
||||
pWindow->addWindowDeco(makeUnique<CHyprGroupBarDecoration>(pWindow));
|
||||
|
||||
if (*PGROUPONMOVETOWORKSPACE && visibleWindowsOnWorkspace == 1 && pFirstWindowOnWorkspace && pFirstWindowOnWorkspace != pWindow && pFirstWindowOnWorkspace->m_group &&
|
||||
pWindow->canBeGroupedInto(pFirstWindowOnWorkspace->m_group)) {
|
||||
pFirstWindowOnWorkspace->m_group->add(pWindow);
|
||||
} else {
|
||||
if (!pWindow->m_isFloating)
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreatedTiling(pWindow);
|
||||
|
||||
if (pWindow->m_isFloating)
|
||||
*pWindow->m_realPosition = POSTOMON + PWORKSPACEMONITOR->m_position;
|
||||
pWindow->layoutTarget()->setPositionGlobal(CBox{POSTOMON + PWORKSPACEMONITOR->m_position, pWindow->layoutTarget()->position().size()});
|
||||
}
|
||||
|
||||
pWindow->updateToplevel();
|
||||
pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
pWindow->uncacheWindowDecos();
|
||||
pWindow->updateGroupOutputs();
|
||||
|
||||
if (!pWindow->m_groupData.pNextWindow.expired()) {
|
||||
PHLWINDOW next = pWindow->m_groupData.pNextWindow.lock();
|
||||
while (next != pWindow) {
|
||||
next->updateToplevel();
|
||||
next = next->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
}
|
||||
if (pWindow->m_group)
|
||||
pWindow->m_group->updateWorkspace(pWorkspace);
|
||||
|
||||
g_layoutManager->newTarget(pWindow->layoutTarget(), pWorkspace->m_space);
|
||||
|
||||
if (FULLSCREEN)
|
||||
setWindowFullscreenInternal(pWindow, FULLSCREENMODE);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <ranges>
|
||||
|
||||
#include "helpers/math/Direction.hpp"
|
||||
#include "managers/XWaylandManager.hpp"
|
||||
#include "managers/KeybindManager.hpp"
|
||||
#include "managers/SessionLockManager.hpp"
|
||||
|
|
@ -113,16 +114,16 @@ class CCompositor {
|
|||
bool isWindowActive(PHLWINDOW);
|
||||
void changeWindowZOrder(PHLWINDOW, bool);
|
||||
void cleanupFadingOut(const MONITORID& monid);
|
||||
PHLWINDOW getWindowInDirection(PHLWINDOW, char);
|
||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, char dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
|
||||
PHLWINDOW getWindowInDirection(PHLWINDOW, Math::eDirection);
|
||||
PHLWINDOW getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow = nullptr, bool useVectorAngles = false);
|
||||
PHLWINDOW getWindowCycle(PHLWINDOW cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool prev = false);
|
||||
PHLWINDOW getWindowCycleHist(PHLWINDOWREF cur, bool focusableOnly = false, std::optional<bool> floating = std::nullopt, bool visible = false, bool next = false);
|
||||
WORKSPACEID getNextAvailableNamedWorkspace();
|
||||
bool isPointOnAnyMonitor(const Vector2D&);
|
||||
bool isPointOnReservedArea(const Vector2D& point, const PHLMONITOR monitor = nullptr);
|
||||
CBox calculateX11WorkArea();
|
||||
PHLMONITOR getMonitorInDirection(const char&);
|
||||
PHLMONITOR getMonitorInDirection(PHLMONITOR, const char&);
|
||||
PHLMONITOR getMonitorInDirection(Math::eDirection);
|
||||
PHLMONITOR getMonitorInDirection(PHLMONITOR, Math::eDirection);
|
||||
void updateAllWindowsAnimatedDecorationValues();
|
||||
MONITORID getNextAvailableMonitorID(std::string const& name);
|
||||
void moveWorkspaceToMonitor(PHLWORKSPACE, PHLMONITOR, bool noWarpCursor = false);
|
||||
|
|
|
|||
|
|
@ -1867,6 +1867,22 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* layout:
|
||||
*/
|
||||
SConfigOptionDescription{
|
||||
.value = "layout:single_window_aspect_ratio",
|
||||
.description = "If specified, whenever only a single window is open, it will be coerced into the specified aspect ratio. Ignored if the y-value is zero.",
|
||||
.type = CONFIG_OPTION_VECTOR,
|
||||
.data = SConfigOptionDescription::SVectorData{{0, 0}, {0, 0}, {1000., 1000.}},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "layout:single_window_aspect_ratio_tolerance",
|
||||
.description = "Minimum distance for single_window_aspect_ratio to take effect, in fractions of the monitor's size.",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.1f, 0.f, 1.f},
|
||||
},
|
||||
|
||||
/*
|
||||
* dwindle:
|
||||
*/
|
||||
|
|
@ -1946,18 +1962,6 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "dwindle:single_window_aspect_ratio",
|
||||
.description = "If specified, whenever only a single window is open, it will be coerced into the specified aspect ratio. Ignored if the y-value is zero.",
|
||||
.type = CONFIG_OPTION_VECTOR,
|
||||
.data = SConfigOptionDescription::SVectorData{{0, 0}, {0, 0}, {1000., 1000.}},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "dwindle:single_window_aspect_ratio_tolerance",
|
||||
.description = "Minimum distance for single_window_aspect_ratio to take effect, in fractions of the monitor's size.",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{0.1f, 0.f, 1.f},
|
||||
},
|
||||
|
||||
/*
|
||||
* master:
|
||||
|
|
@ -2043,6 +2047,53 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
|
|||
.data = SConfigOptionDescription::SBoolData{false},
|
||||
},
|
||||
|
||||
/*
|
||||
* scrolling:
|
||||
*/
|
||||
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:fullscreen_on_one_column",
|
||||
.description = "when enabled, a single column on a workspace will always span the entire screen.",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:column_width",
|
||||
.description = "the default width of a column, [0.1 - 1.0].",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{.value = 0.5, .min = 0.1, .max = 1.0},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:focus_fit_method",
|
||||
.description = "When a column is focused, what method should be used to bring it into view",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "center,fit"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:follow_focus",
|
||||
.description = "when a window is focused, should the layout move to bring it into view automatically",
|
||||
.type = CONFIG_OPTION_BOOL,
|
||||
.data = SConfigOptionDescription::SBoolData{.value = true},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:follow_min_visible",
|
||||
.description = "when a window is focused, require that at least a given fraction of it is visible for focus to follow",
|
||||
.type = CONFIG_OPTION_FLOAT,
|
||||
.data = SConfigOptionDescription::SFloatData{.value = 0.4, .min = 0.0, .max = 1.0},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:explicit_column_widths",
|
||||
.description = "A comma-separated list of preconfigured widths for colresize +conf/-conf",
|
||||
.type = CONFIG_OPTION_STRING_SHORT,
|
||||
.data = SConfigOptionDescription::SStringData{"0.333, 0.5, 0.667, 1.0"},
|
||||
},
|
||||
SConfigOptionDescription{
|
||||
.value = "scrolling:direction",
|
||||
.description = "Direction in which new windows appear and the layout scrolls",
|
||||
.type = CONFIG_OPTION_CHOICE,
|
||||
.data = SConfigOptionDescription::SChoiceData{.firstIndex = 0, .choices = "right,left,down,up"},
|
||||
},
|
||||
|
||||
/*
|
||||
* Quirks
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@
|
|||
#include "../desktop/rule/layerRule/LayerRule.hpp"
|
||||
#include "../debug/HyprCtl.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
#include "defaultConfig.hpp"
|
||||
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../hyprerror/HyprError.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
#include "../managers/permissions/DynamicPermissionManager.hpp"
|
||||
#include "../debug/HyprNotificationOverlay.hpp"
|
||||
|
|
@ -616,6 +617,9 @@ CConfigManager::CConfigManager() {
|
|||
registerConfigVar("decoration:screen_shader", {STRVAL_EMPTY});
|
||||
registerConfigVar("decoration:border_part_of_window", Hyprlang::INT{1});
|
||||
|
||||
registerConfigVar("layout:single_window_aspect_ratio", Hyprlang::VEC2{0, 0});
|
||||
registerConfigVar("layout:single_window_aspect_ratio_tolerance", {0.1f});
|
||||
|
||||
registerConfigVar("dwindle:pseudotile", Hyprlang::INT{0});
|
||||
registerConfigVar("dwindle:force_split", Hyprlang::INT{0});
|
||||
registerConfigVar("dwindle:permanent_direction_override", Hyprlang::INT{0});
|
||||
|
|
@ -628,8 +632,6 @@ CConfigManager::CConfigManager() {
|
|||
registerConfigVar("dwindle:smart_split", Hyprlang::INT{0});
|
||||
registerConfigVar("dwindle:smart_resizing", Hyprlang::INT{1});
|
||||
registerConfigVar("dwindle:precise_mouse_move", Hyprlang::INT{0});
|
||||
registerConfigVar("dwindle:single_window_aspect_ratio", Hyprlang::VEC2{0, 0});
|
||||
registerConfigVar("dwindle:single_window_aspect_ratio_tolerance", {0.1f});
|
||||
|
||||
registerConfigVar("master:special_scale_factor", {1.f});
|
||||
registerConfigVar("master:mfact", {0.55f});
|
||||
|
|
@ -645,6 +647,14 @@ CConfigManager::CConfigManager() {
|
|||
registerConfigVar("master:drop_at_cursor", Hyprlang::INT{1});
|
||||
registerConfigVar("master:always_keep_position", Hyprlang::INT{0});
|
||||
|
||||
registerConfigVar("scrolling:fullscreen_on_one_column", Hyprlang::INT{1});
|
||||
registerConfigVar("scrolling:column_width", Hyprlang::FLOAT{0.5F});
|
||||
registerConfigVar("scrolling:focus_fit_method", Hyprlang::INT{1});
|
||||
registerConfigVar("scrolling:follow_focus", Hyprlang::INT{1});
|
||||
registerConfigVar("scrolling:follow_min_visible", Hyprlang::FLOAT{0.4});
|
||||
registerConfigVar("scrolling:explicit_column_widths", Hyprlang::STRING{"0.333, 0.5, 0.667, 1.0"});
|
||||
registerConfigVar("scrolling:direction", Hyprlang::STRING{"right"});
|
||||
|
||||
registerConfigVar("animations:enabled", Hyprlang::INT{1});
|
||||
registerConfigVar("animations:workspace_wraparound", Hyprlang::INT{0});
|
||||
|
||||
|
|
@ -715,9 +725,9 @@ CConfigManager::CConfigManager() {
|
|||
registerConfigVar("binds:movefocus_cycles_fullscreen", Hyprlang::INT{0});
|
||||
registerConfigVar("binds:movefocus_cycles_groupfirst", Hyprlang::INT{0});
|
||||
registerConfigVar("binds:disable_keybind_grabbing", Hyprlang::INT{0});
|
||||
registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
|
||||
registerConfigVar("binds:allow_pin_fullscreen", Hyprlang::INT{0});
|
||||
registerConfigVar("binds:drag_threshold", Hyprlang::INT{0});
|
||||
registerConfigVar("binds:window_direction_monitor_fallback", Hyprlang::INT{1});
|
||||
|
||||
registerConfigVar("gestures:workspace_swipe_distance", Hyprlang::INT{300});
|
||||
registerConfigVar("gestures:workspace_swipe_invert", Hyprlang::INT{1});
|
||||
|
|
@ -1338,7 +1348,8 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
|||
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
*(m->m_cursorZoom) = *PZOOMFACTOR;
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
||||
if (m->m_activeWorkspace)
|
||||
m->m_activeWorkspace->m_space->recalculate();
|
||||
}
|
||||
|
||||
// Update the keyboard layout to the cfg'd one if this is not the first launch
|
||||
|
|
@ -1406,9 +1417,6 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
|||
// Update window border colors
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
// update layout
|
||||
g_pLayoutManager->switchToLayout(std::any_cast<Hyprlang::STRING>(m_config->getConfigValue("general:layout")));
|
||||
|
||||
// manual crash
|
||||
if (std::any_cast<Hyprlang::INT>(m_config->getConfigValue("debug:manual_crash")) && !m_manualCrashInitiated) {
|
||||
m_manualCrashInitiated = true;
|
||||
|
|
@ -1447,6 +1455,9 @@ void CConfigManager::postConfigReload(const Hyprlang::CParseResult& result) {
|
|||
if (!m_isFirstLaunch)
|
||||
ensurePersistentWorkspacesPresent();
|
||||
|
||||
// update layouts
|
||||
Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts();
|
||||
|
||||
EMIT_HOOK_EVENT("configReloaded", nullptr);
|
||||
if (g_pEventManager)
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"configreloaded", ""});
|
||||
|
|
@ -1469,8 +1480,9 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
|
|||
|
||||
// invalidate layouts if they changed
|
||||
if (COMMAND == "monitor" || COMMAND.contains("gaps_") || COMMAND.starts_with("dwindle:") || COMMAND.starts_with("master:")) {
|
||||
for (auto const& m : g_pCompositor->m_monitors)
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
g_layoutManager->recalculateMonitor(m);
|
||||
}
|
||||
}
|
||||
|
||||
// Update window border colors
|
||||
|
|
@ -1642,6 +1654,8 @@ SWorkspaceRule CConfigManager::mergeWorkspaceRules(const SWorkspaceRule& rule1,
|
|||
mergedRule.onCreatedEmptyRunCmd = rule2.onCreatedEmptyRunCmd;
|
||||
if (rule2.defaultName.has_value())
|
||||
mergedRule.defaultName = rule2.defaultName;
|
||||
if (rule2.layout.has_value())
|
||||
mergedRule.layout = rule2.layout;
|
||||
if (!rule2.layoutopts.empty()) {
|
||||
for (const auto& layoutopt : rule2.layoutopts) {
|
||||
mergedRule.layoutopts[layoutopt.first] = layoutopt.second;
|
||||
|
|
@ -2710,6 +2724,9 @@ std::optional<std::string> CConfigManager::handleWorkspaceRules(const std::strin
|
|||
opt = opt.substr(0, opt.find(':'));
|
||||
|
||||
wsRule.layoutopts[opt] = val;
|
||||
} else if ((delim = rule.find("layout:")) != std::string::npos) {
|
||||
std::string layout = rule.substr(delim + 7);
|
||||
wsRule.layout = std::move(layout);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
@ -3072,20 +3089,20 @@ std::string SConfigOptionDescription::jsonify() const {
|
|||
else if (typeid(Hyprlang::FLOAT) == std::type_index(CONFIGVALUE.type()))
|
||||
currentValue = std::format("{:.2f}", std::any_cast<Hyprlang::FLOAT>(CONFIGVALUE));
|
||||
else if (typeid(Hyprlang::STRING) == std::type_index(CONFIGVALUE.type()))
|
||||
currentValue = std::any_cast<Hyprlang::STRING>(CONFIGVALUE);
|
||||
currentValue = std::format("\"{}\"", std::any_cast<Hyprlang::STRING>(CONFIGVALUE));
|
||||
else if (typeid(Hyprlang::VEC2) == std::type_index(CONFIGVALUE.type())) {
|
||||
const auto V = std::any_cast<Hyprlang::VEC2>(CONFIGVALUE);
|
||||
currentValue = std::format("{}, {}", V.x, V.y);
|
||||
currentValue = std::format("\"{}, {}\"", V.x, V.y);
|
||||
} else if (typeid(void*) == std::type_index(CONFIGVALUE.type())) {
|
||||
const auto DATA = sc<ICustomConfigValueData*>(std::any_cast<void*>(CONFIGVALUE));
|
||||
currentValue = DATA->toString();
|
||||
currentValue = std::format("\"{}\"", DATA->toString());
|
||||
}
|
||||
|
||||
try {
|
||||
using T = std::decay_t<decltype(val)>;
|
||||
if constexpr (std::is_same_v<T, SStringData>) {
|
||||
return std::format(R"#( "value": "{}",
|
||||
"current": "{}",
|
||||
"current": {},
|
||||
"explicit": {})#",
|
||||
val.value, currentValue, EXPLICIT);
|
||||
} else if constexpr (std::is_same_v<T, SRangeData>) {
|
||||
|
|
@ -3104,7 +3121,7 @@ std::string SConfigOptionDescription::jsonify() const {
|
|||
val.value, val.min, val.max, currentValue, EXPLICIT);
|
||||
} else if constexpr (std::is_same_v<T, SColorData>) {
|
||||
return std::format(R"#( "value": "{}",
|
||||
"current": "{}",
|
||||
"current": {},
|
||||
"explicit": {})#",
|
||||
val.color.getAsHex(), currentValue, EXPLICIT);
|
||||
} else if constexpr (std::is_same_v<T, SBoolData>) {
|
||||
|
|
@ -3125,12 +3142,12 @@ std::string SConfigOptionDescription::jsonify() const {
|
|||
"min_y": {},
|
||||
"max_x": {},
|
||||
"max_y": {},
|
||||
"current": "{}",
|
||||
"current": {},
|
||||
"explicit": {})#",
|
||||
val.vec.x, val.vec.y, val.min.x, val.min.y, val.max.x, val.max.y, currentValue, EXPLICIT);
|
||||
} else if constexpr (std::is_same_v<T, SGradientData>) {
|
||||
return std::format(R"#( "value": "{}",
|
||||
"current": "{}",
|
||||
"current": {},
|
||||
"explicit": {})#",
|
||||
val.gradient, currentValue, EXPLICIT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct SWorkspaceRule {
|
|||
std::optional<bool> noShadow;
|
||||
std::optional<std::string> onCreatedEmptyRunCmd;
|
||||
std::optional<std::string> defaultName;
|
||||
std::optional<std::string> layout;
|
||||
std::map<std::string, std::string> layoutopts;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ using namespace Hyprutils::OS;
|
|||
#include "config/ConfigManager.hpp"
|
||||
#include "helpers/MiscFunctions.hpp"
|
||||
#include "../desktop/view/LayerSurface.hpp"
|
||||
#include "../desktop/view/Group.hpp"
|
||||
#include "../desktop/rule/Engine.hpp"
|
||||
#include "../desktop/history/WindowHistoryTracker.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
|
|
@ -52,12 +53,15 @@ using namespace Hyprutils::OS;
|
|||
#include "../Compositor.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/XWaylandManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../plugins/PluginSystem.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../debug/HyprNotificationOverlay.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../render/OpenGL.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/algorithm/Algorithm.hpp"
|
||||
#include "../layout/algorithm/TiledAlgorithm.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/ucred.h>
|
||||
|
|
@ -330,23 +334,19 @@ static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
|
||||
static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
||||
const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON;
|
||||
if (w->m_groupData.pNextWindow.expired())
|
||||
if (!w->m_group)
|
||||
return isJson ? "" : "0";
|
||||
|
||||
std::ostringstream result;
|
||||
|
||||
PHLWINDOW head = w->getGroupHead();
|
||||
PHLWINDOW curr = head;
|
||||
while (true) {
|
||||
for (const auto& curr : w->m_group->windows()) {
|
||||
if (isJson)
|
||||
result << std::format("\"0x{:x}\"", rc<uintptr_t>(curr.get()));
|
||||
else
|
||||
result << std::format("{:x}", rc<uintptr_t>(curr.get()));
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
// We've wrapped around to the start, break out without trailing comma
|
||||
if (curr == head)
|
||||
break;
|
||||
result << (isJson ? ", " : ",");
|
||||
|
||||
if (curr != w->m_group->windows().back())
|
||||
result << (isJson ? ", " : ",");
|
||||
}
|
||||
|
||||
return result.str();
|
||||
|
|
@ -375,7 +375,6 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
"name": "{}"
|
||||
}},
|
||||
"floating": {},
|
||||
"pseudo": {},
|
||||
"monitor": {},
|
||||
"class": "{}",
|
||||
"title": "{}",
|
||||
|
|
@ -399,15 +398,15 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
}},)#",
|
||||
rc<uintptr_t>(w.get()), (w->m_isMapped ? "true" : "false"), (w->isHidden() ? "true" : "false"), sc<int>(w->m_realPosition->goal().x),
|
||||
sc<int>(w->m_realPosition->goal().y), sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||
escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc<int>(w->m_isFloating) == 1 ? "true" : "false"), (w->m_isPseudotiled ? "true" : "false"),
|
||||
w->monitorID(), escapeJSONStrings(w->m_class), escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(),
|
||||
(sc<int>(w->m_isX11) == 1 ? "true" : "false"), (w->m_pinned ? "true" : "false"), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
||||
(w->m_createdOverFullscreen ? "true" : "false"), getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
||||
escapeJSONStrings(!w->m_workspace ? "" : w->m_workspace->m_name), (sc<int>(w->m_isFloating) == 1 ? "true" : "false"), w->monitorID(), escapeJSONStrings(w->m_class),
|
||||
escapeJSONStrings(w->m_title), escapeJSONStrings(w->m_initialClass), escapeJSONStrings(w->m_initialTitle), w->getPID(), (sc<int>(w->m_isX11) == 1 ? "true" : "false"),
|
||||
(w->m_pinned ? "true" : "false"), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client), (w->m_createdOverFullscreen ? "true" : "false"),
|
||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
||||
(g_pInputManager->isWindowInhibiting(w, false) ? "true" : "false"), escapeJSONStrings(w->xdgTag().value_or("")), escapeJSONStrings(w->xdgDescription().value_or("")),
|
||||
escapeJSONStrings(NContentType::toString(w->getContentType())), w->m_stableID);
|
||||
} else {
|
||||
return std::format(
|
||||
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tpseudo: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
||||
"Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: "
|
||||
"{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: "
|
||||
"{}\n\txwayland: {}\n\tpinned: "
|
||||
"{}\n\tfullscreen: {}\n\tfullscreenClient: {}\n\toverFullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\tinhibitingIdle: "
|
||||
|
|
@ -415,11 +414,10 @@ std::string CHyprCtl::getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) {
|
|||
"{}\n\txdgDescription: {}\n\tcontentType: {}\n\tstableID: {:x}\n\n",
|
||||
rc<uintptr_t>(w.get()), w->m_title, sc<int>(w->m_isMapped), sc<int>(w->isHidden()), sc<int>(w->m_realPosition->goal().x), sc<int>(w->m_realPosition->goal().y),
|
||||
sc<int>(w->m_realSize->goal().x), sc<int>(w->m_realSize->goal().y), w->m_workspace ? w->workspaceID() : WORKSPACE_INVALID,
|
||||
(!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), sc<int>(w->m_isPseudotiled), w->monitorID(), w->m_class, w->m_title, w->m_initialClass,
|
||||
w->m_initialTitle, w->getPID(), sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client),
|
||||
sc<int>(w->m_createdOverFullscreen), getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w),
|
||||
sc<int>(g_pInputManager->isWindowInhibiting(w, false)), w->xdgTag().value_or(""), w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()),
|
||||
w->m_stableID);
|
||||
(!w->m_workspace ? "" : w->m_workspace->m_name), sc<int>(w->m_isFloating), w->monitorID(), w->m_class, w->m_title, w->m_initialClass, w->m_initialTitle, w->getPID(),
|
||||
sc<int>(w->m_isX11), sc<int>(w->m_pinned), sc<uint8_t>(w->m_fullscreenState.internal), sc<uint8_t>(w->m_fullscreenState.client), sc<int>(w->m_createdOverFullscreen),
|
||||
getGroupedData(w, format), getTagsData(w, format), rc<uintptr_t>(w->m_swallowed.get()), getFocusHistoryID(w), sc<int>(g_pInputManager->isWindowInhibiting(w, false)),
|
||||
w->xdgTag().value_or(""), w->xdgDescription().value_or(""), NContentType::toString(w->getContentType()), w->m_stableID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -453,8 +451,15 @@ static std::string clientsRequest(eHyprCtlOutputFormat format, std::string reque
|
|||
}
|
||||
|
||||
std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat format) {
|
||||
const auto PLASTW = w->getLastFocusedWindow();
|
||||
const auto PMONITOR = w->m_monitor.lock();
|
||||
const auto PLASTW = w->getLastFocusedWindow();
|
||||
const auto PMONITOR = w->m_monitor.lock();
|
||||
|
||||
std::string layoutName = "unknown";
|
||||
if (w->m_space && w->m_space->algorithm() && w->m_space->algorithm()->tiledAlgo()) {
|
||||
const auto& TILED_ALGO = w->m_space->algorithm()->tiledAlgo();
|
||||
layoutName = Layout::Supplementary::algoMatcher()->getNameForTiledAlgo(&typeid(*TILED_ALGO.get()));
|
||||
}
|
||||
|
||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
||||
return std::format(R"#({{
|
||||
"id": {},
|
||||
|
|
@ -465,16 +470,17 @@ std::string CHyprCtl::getWorkspaceData(PHLWORKSPACE w, eHyprCtlOutputFormat form
|
|||
"hasfullscreen": {},
|
||||
"lastwindow": "0x{:x}",
|
||||
"lastwindowtitle": "{}",
|
||||
"ispersistent": {}
|
||||
"ispersistent": {},
|
||||
"tiledLayout": "{}"
|
||||
}})#",
|
||||
w->m_id, escapeJSONStrings(w->m_name), escapeJSONStrings(PMONITOR ? PMONITOR->m_name : "?"),
|
||||
escapeJSONStrings(PMONITOR ? std::to_string(PMONITOR->m_id) : "null"), w->getWindows(), w->m_hasFullscreenWindow ? "true" : "false",
|
||||
rc<uintptr_t>(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false");
|
||||
rc<uintptr_t>(PLASTW.get()), PLASTW ? escapeJSONStrings(PLASTW->m_title) : "", w->isPersistent() ? "true" : "false", escapeJSONStrings(layoutName));
|
||||
} else {
|
||||
return std::format(
|
||||
"workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: {}\n\n",
|
||||
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(), sc<int>(w->m_hasFullscreenWindow),
|
||||
rc<uintptr_t>(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc<int>(w->isPersistent()));
|
||||
return std::format("workspace ID {} ({}) on monitor {}:\n\tmonitorID: {}\n\twindows: {}\n\thasfullscreen: {}\n\tlastwindow: 0x{:x}\n\tlastwindowtitle: {}\n\tispersistent: "
|
||||
"{}\n\ttiledLayout: {}\n\n",
|
||||
w->m_id, w->m_name, PMONITOR ? PMONITOR->m_name : "?", PMONITOR ? std::to_string(PMONITOR->m_id) : "null", w->getWindows(),
|
||||
sc<int>(w->m_hasFullscreenWindow), rc<uintptr_t>(PLASTW.get()), PLASTW ? PLASTW->m_title : "", sc<int>(w->isPersistent()), layoutName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -673,28 +679,6 @@ static std::string layersRequest(eHyprCtlOutputFormat format, std::string reques
|
|||
return result;
|
||||
}
|
||||
|
||||
static std::string layoutsRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
std::string result = "";
|
||||
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
|
||||
result += "[";
|
||||
|
||||
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
|
||||
result += std::format(
|
||||
R"#(
|
||||
"{}",)#",
|
||||
m);
|
||||
}
|
||||
trimTrailingComma(result);
|
||||
|
||||
result += "\n]\n";
|
||||
} else {
|
||||
for (auto const& m : g_pLayoutManager->getAllLayoutNames()) {
|
||||
result += std::format("{}\n", m);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string configErrorsRequest(eHyprCtlOutputFormat format, std::string request) {
|
||||
std::string result = "";
|
||||
std::string currErrors = g_pConfigManager->getErrors();
|
||||
|
|
@ -1316,10 +1300,8 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
|||
g_pInputManager->setTabletConfigs(); // update tablets
|
||||
}
|
||||
|
||||
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
|
||||
|
||||
if (COMMAND.contains("general:layout"))
|
||||
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
|
||||
if (COMMAND.contains("general:layout") || (COMMAND.contains("workspace") && VALUE.contains("layout:")))
|
||||
Layout::Supplementary::algoMatcher()->updateWorkspaceLayouts();
|
||||
|
||||
if (COMMAND.contains("decoration:screen_shader") || COMMAND == "source")
|
||||
g_pHyprOpenGL->m_reloadScreenShader = true;
|
||||
|
|
@ -1339,7 +1321,8 @@ static std::string dispatchKeyword(eHyprCtlOutputFormat format, std::string in)
|
|||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
*(m->m_cursorZoom) = *PZOOMFACTOR;
|
||||
g_pHyprRenderer->damageMonitor(m);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
||||
if (m->m_activeWorkspace)
|
||||
m->m_activeWorkspace->m_space->recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1613,7 +1596,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
|||
static auto PGROUPACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_active");
|
||||
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue<Hyprlang::CUSTOMTYPE>("group:col.border_locked_inactive");
|
||||
|
||||
const bool GROUPLOCKED = PWINDOW->m_groupData.pNextWindow.lock() ? PWINDOW->getGroupHead()->m_groupData.locked : false;
|
||||
const bool GROUPLOCKED = PWINDOW->m_group ? PWINDOW->m_group->locked() : false;
|
||||
|
||||
if (active) {
|
||||
auto* const ACTIVECOL = (CGradientValueData*)(PACTIVECOL.ptr())->getData();
|
||||
|
|
@ -1621,7 +1604,7 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
|||
auto* const GROUPACTIVECOL = (CGradientValueData*)(PGROUPACTIVECOL.ptr())->getData();
|
||||
auto* const GROUPACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPACTIVELOCKEDCOL.ptr())->getData();
|
||||
const auto* const ACTIVECOLOR =
|
||||
!PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
||||
!PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
||||
|
||||
std::string borderColorString = PWINDOW->m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR).toString();
|
||||
if (FORMNORM)
|
||||
|
|
@ -1633,8 +1616,8 @@ static std::string dispatchGetProp(eHyprCtlOutputFormat format, std::string requ
|
|||
auto* const NOGROUPINACTIVECOL = (CGradientValueData*)(PNOGROUPINACTIVECOL.ptr())->getData();
|
||||
auto* const GROUPINACTIVECOL = (CGradientValueData*)(PGROUPINACTIVECOL.ptr())->getData();
|
||||
auto* const GROUPINACTIVELOCKEDCOL = (CGradientValueData*)(PGROUPINACTIVELOCKEDCOL.ptr())->getData();
|
||||
const auto* const INACTIVECOLOR = !PWINDOW->m_groupData.pNextWindow.lock() ? (!PWINDOW->m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) :
|
||||
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
||||
const auto* const INACTIVECOLOR = !PWINDOW->m_group ? (!(PWINDOW->m_groupRules & Desktop::View::GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) :
|
||||
(GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
||||
|
||||
std::string borderColorString = PWINDOW->m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR).toString();
|
||||
if (FORMNORM)
|
||||
|
|
@ -2089,7 +2072,6 @@ CHyprCtl::CHyprCtl() {
|
|||
registerCommand(SHyprCtlCommand{"systeminfo", true, systemInfoRequest});
|
||||
registerCommand(SHyprCtlCommand{"animations", true, animationsRequest});
|
||||
registerCommand(SHyprCtlCommand{"rollinglog", true, rollinglogRequest});
|
||||
registerCommand(SHyprCtlCommand{"layouts", true, layoutsRequest});
|
||||
registerCommand(SHyprCtlCommand{"configerrors", true, configErrorsRequest});
|
||||
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
|
||||
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
|
||||
|
|
@ -2202,10 +2184,6 @@ std::string CHyprCtl::getReply(std::string request) {
|
|||
g_pInputManager->setTouchDeviceConfigs(); // update touch device cfgs
|
||||
g_pInputManager->setTabletConfigs(); // update tablets
|
||||
|
||||
static auto PLAYOUT = CConfigValue<std::string>("general:layout");
|
||||
|
||||
g_pLayoutManager->switchToLayout(*PLAYOUT); // update layout
|
||||
|
||||
g_pHyprOpenGL->m_reloadScreenShader = true;
|
||||
|
||||
for (auto& [m, rd] : g_pHyprOpenGL->m_monitorRenderResources) {
|
||||
|
|
@ -2221,7 +2199,6 @@ std::string CHyprCtl::getReply(std::string request) {
|
|||
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
g_pHyprRenderer->damageMonitor(m);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,3 +5,7 @@
|
|||
#include "helpers/Color.hpp"
|
||||
#include "macros.hpp"
|
||||
#include "desktop/DesktopTypes.hpp"
|
||||
|
||||
#if !defined(__GXX_RTTI)
|
||||
#error "Hyprland requires C++ RTTI. Shit will hit the fan otherwise. Do not even try."
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#include "Workspace.hpp"
|
||||
#include "view/Group.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../config/ConfigValue.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "managers/animation/AnimationManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
|
||||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
|
@ -41,6 +44,9 @@ void CWorkspace::init(PHLWORKSPACE self) {
|
|||
m_lastFocusedWindow.reset();
|
||||
});
|
||||
|
||||
m_space = Layout::CSpace::create(m_self.lock());
|
||||
m_space->setAlgorithmProvider(Layout::Supplementary::algoMatcher()->createAlgorithmForWorkspace(m_self.lock()));
|
||||
|
||||
m_inert = false;
|
||||
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(self);
|
||||
|
|
@ -406,14 +412,19 @@ bool CWorkspace::isVisibleNotCovered() {
|
|||
|
||||
int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
|
||||
int no = 0;
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->workspaceID() != m_id || !w->m_isMapped)
|
||||
|
||||
if (!m_space)
|
||||
return 0;
|
||||
|
||||
for (auto const& t : m_space->targets()) {
|
||||
if (!t)
|
||||
continue;
|
||||
if (onlyTiled.has_value() && w->m_isFloating == onlyTiled.value())
|
||||
|
||||
if (onlyTiled.has_value() && t->floating() == onlyTiled.value())
|
||||
continue;
|
||||
if (onlyPinned.has_value() && w->m_pinned != onlyPinned.value())
|
||||
if (onlyPinned.has_value() && (!t->window() || t->window()->m_pinned != onlyPinned.value()))
|
||||
continue;
|
||||
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
|
||||
if (onlyVisible.has_value() && (!t->window() || t->window()->isHidden() == onlyVisible.value()))
|
||||
continue;
|
||||
no++;
|
||||
}
|
||||
|
|
@ -423,16 +434,16 @@ int CWorkspace::getWindows(std::optional<bool> onlyTiled, std::optional<bool> on
|
|||
|
||||
int CWorkspace::getGroups(std::optional<bool> onlyTiled, std::optional<bool> onlyPinned, std::optional<bool> onlyVisible) {
|
||||
int no = 0;
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (w->workspaceID() != m_id || !w->m_isMapped)
|
||||
for (auto const& g : Desktop::View::groups()) {
|
||||
const auto HEAD = g->head();
|
||||
|
||||
if (HEAD->workspaceID() != m_id || !HEAD->m_isMapped)
|
||||
continue;
|
||||
if (!w->m_groupData.head)
|
||||
if (onlyTiled.has_value() && HEAD->m_isFloating == onlyTiled.value())
|
||||
continue;
|
||||
if (onlyTiled.has_value() && w->m_isFloating == onlyTiled.value())
|
||||
if (onlyPinned.has_value() && HEAD->m_pinned != onlyPinned.value())
|
||||
continue;
|
||||
if (onlyPinned.has_value() && w->m_pinned != onlyPinned.value())
|
||||
continue;
|
||||
if (onlyVisible.has_value() && w->isHidden() == onlyVisible.value())
|
||||
if (onlyVisible.has_value() && g->current()->isHidden() == onlyVisible.value())
|
||||
continue;
|
||||
no++;
|
||||
}
|
||||
|
|
@ -514,13 +525,11 @@ void CWorkspace::rename(const std::string& name) {
|
|||
}
|
||||
|
||||
void CWorkspace::updateWindows() {
|
||||
m_hasFullscreenWindow = std::ranges::any_of(g_pCompositor->m_windows, [this](const auto& w) { return w->m_isMapped && w->m_workspace == m_self && w->isFullscreen(); });
|
||||
m_hasFullscreenWindow = std::ranges::any_of(m_space->targets(), [](const auto& t) { return t->fullscreenMode() != FSMODE_NONE; });
|
||||
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (!w->m_isMapped || w->m_workspace != m_self)
|
||||
continue;
|
||||
|
||||
w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
for (auto const& t : m_space->targets()) {
|
||||
if (t->window())
|
||||
t->window()->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@
|
|||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
namespace Layout {
|
||||
class CSpace;
|
||||
};
|
||||
|
||||
enum eFullscreenMode : int8_t {
|
||||
FSMODE_NONE = 0,
|
||||
FSMODE_MAXIMIZED = 1 << 0,
|
||||
|
|
@ -20,7 +24,9 @@ class CWorkspace {
|
|||
CWorkspace(WORKSPACEID id, PHLMONITOR monitor, std::string name, bool special = false, bool isEmpty = true);
|
||||
~CWorkspace();
|
||||
|
||||
WP<CWorkspace> m_self;
|
||||
WP<CWorkspace> m_self;
|
||||
|
||||
SP<Layout::CSpace> m_space;
|
||||
|
||||
// Workspaces ID-based have IDs > 0
|
||||
// and workspaces name-based have IDs starting with -1337
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ CWindowHistoryTracker::CWindowHistoryTracker() {
|
|||
});
|
||||
|
||||
static auto P1 = g_pHookSystem->hookDynamic("activeWindow", [this](void* self, SCallbackInfo& info, std::any data) {
|
||||
auto window = std::any_cast<PHLWINDOW>(data);
|
||||
auto window = std::any_cast<Desktop::View::SWindowActiveEvent>(data).window;
|
||||
|
||||
track(window);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ bool CWindowRule::matches(PHLWINDOW w, bool allowEnvLookup) {
|
|||
return false;
|
||||
break;
|
||||
case RULE_PROP_GROUP:
|
||||
if (!engine->match(w->m_groupData.pNextWindow))
|
||||
if (!engine->match(!!w->m_group))
|
||||
return false;
|
||||
break;
|
||||
case RULE_PROP_MODAL:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#include "../utils/SetUtils.hpp"
|
||||
#include "../../view/Window.hpp"
|
||||
#include "../../types/OverridableVar.hpp"
|
||||
#include "../../../managers/LayoutManager.hpp"
|
||||
#include "../../../managers/HookSystemManager.hpp"
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,19 @@
|
|||
#include "../../Compositor.hpp"
|
||||
#include "../../protocols/XDGShell.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include "../../managers/LayoutManager.hpp"
|
||||
#include "../../managers/EventManager.hpp"
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../managers/SeatManager.hpp"
|
||||
#include "../../xwayland/XSurface.hpp"
|
||||
#include "../../protocols/PointerConstraints.hpp"
|
||||
#include "managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
|
||||
using namespace Desktop;
|
||||
|
||||
#define COMMA ,
|
||||
|
||||
SP<CFocusState> Desktop::focusState() {
|
||||
static SP<CFocusState> state = makeShared<CFocusState>();
|
||||
return state;
|
||||
|
|
@ -63,7 +67,7 @@ static SFullscreenWorkspaceFocusResult onFullscreenWorkspaceFocusWindow(PHLWINDO
|
|||
return {};
|
||||
}
|
||||
|
||||
void CFocusState::fullWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surface, bool forceFSCycle) {
|
||||
void CFocusState::fullWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP<CWLSurfaceResource> surface, bool forceFSCycle) {
|
||||
if (pWindow) {
|
||||
if (!pWindow->m_workspace)
|
||||
return;
|
||||
|
|
@ -83,10 +87,10 @@ void CFocusState::fullWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surf
|
|||
return;
|
||||
}
|
||||
|
||||
rawWindowFocus(pWindow, surface);
|
||||
rawWindowFocus(pWindow, reason, surface);
|
||||
}
|
||||
|
||||
void CFocusState::rawWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surface) {
|
||||
void CFocusState::rawWindowFocus(PHLWINDOW pWindow, eFocusReason reason, SP<CWLSurfaceResource> surface) {
|
||||
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
|
||||
static auto PSPECIALFALLTHROUGH = CConfigValue<Hyprlang::INT>("input:special_fallthrough");
|
||||
|
||||
|
|
@ -105,7 +109,9 @@ void CFocusState::rawWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surfa
|
|||
if (pWindow && pWindow->m_isX11 && pWindow->isX11OverrideRedirect() && !pWindow->m_xwaylandSurface->wantsFocus())
|
||||
return;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow);
|
||||
// m_target on purpose, this avoids the group
|
||||
if (pWindow)
|
||||
g_layoutManager->bringTargetToTop(pWindow->m_target);
|
||||
|
||||
if (!pWindow || !validMapped(pWindow)) {
|
||||
|
||||
|
|
@ -127,9 +133,7 @@ void CFocusState::rawWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surfa
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
|
||||
|
||||
EMIT_HOOK_EVENT("activeWindow", PHLWINDOW{nullptr});
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(nullptr);
|
||||
EMIT_HOOK_EVENT("activeWindow", Desktop::View::SWindowActiveEvent{nullptr COMMA reason});
|
||||
|
||||
m_focusSurface.reset();
|
||||
|
||||
|
|
@ -196,16 +200,14 @@ void CFocusState::rawWindowFocus(PHLWINDOW pWindow, SP<CWLSurfaceResource> surfa
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = pWindow->m_class + "," + pWindow->m_title});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc<uintptr_t>(pWindow.get()))});
|
||||
|
||||
EMIT_HOOK_EVENT("activeWindow", pWindow);
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(pWindow);
|
||||
EMIT_HOOK_EVENT("activeWindow", Desktop::View::SWindowActiveEvent{pWindow COMMA reason});
|
||||
|
||||
g_pInputManager->recheckIdleInhibitorStatus();
|
||||
|
||||
if (*PFOLLOWMOUSE == 0)
|
||||
g_pInputManager->sendMotionEventsToFocused();
|
||||
|
||||
if (pWindow->m_groupData.pNextWindow)
|
||||
if (pWindow->m_group)
|
||||
pWindow->deactivateGroupMembers();
|
||||
}
|
||||
|
||||
|
|
@ -296,3 +298,7 @@ void CFocusState::resetWindowFocus() {
|
|||
m_focusWindow.reset();
|
||||
m_focusSurface.reset();
|
||||
}
|
||||
|
||||
bool Desktop::isHardInputFocusReason(eFocusReason r) {
|
||||
return r == FOCUS_REASON_NEW_WINDOW || r == FOCUS_REASON_KEYBIND || r == FOCUS_REASON_GHOSTS || r == FOCUS_REASON_CLICK || r == FOCUS_REASON_DESKTOP_STATE_CHANGE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,19 @@
|
|||
class CWLSurfaceResource;
|
||||
|
||||
namespace Desktop {
|
||||
enum eFocusReason : uint8_t {
|
||||
FOCUS_REASON_UNKNOWN = 0,
|
||||
FOCUS_REASON_FFM,
|
||||
FOCUS_REASON_KEYBIND,
|
||||
FOCUS_REASON_CLICK,
|
||||
FOCUS_REASON_OTHER,
|
||||
FOCUS_REASON_DESKTOP_STATE_CHANGE,
|
||||
FOCUS_REASON_NEW_WINDOW,
|
||||
FOCUS_REASON_GHOSTS,
|
||||
};
|
||||
|
||||
bool isHardInputFocusReason(eFocusReason r);
|
||||
|
||||
class CFocusState {
|
||||
public:
|
||||
CFocusState();
|
||||
|
|
@ -15,8 +28,8 @@ namespace Desktop {
|
|||
CFocusState(CFocusState&) = delete;
|
||||
CFocusState(const CFocusState&) = delete;
|
||||
|
||||
void fullWindowFocus(PHLWINDOW w, SP<CWLSurfaceResource> surface = nullptr, bool forceFSCycle = false);
|
||||
void rawWindowFocus(PHLWINDOW w, SP<CWLSurfaceResource> surface = nullptr);
|
||||
void fullWindowFocus(PHLWINDOW w, eFocusReason reason, SP<CWLSurfaceResource> surface = nullptr, bool forceFSCycle = false);
|
||||
void rawWindowFocus(PHLWINDOW w, eFocusReason reason, SP<CWLSurfaceResource> surface = nullptr);
|
||||
void rawSurfaceFocus(SP<CWLSurfaceResource> s, PHLWINDOW pWindowOwner = nullptr);
|
||||
void rawMonitorFocus(PHLMONITOR m);
|
||||
|
||||
|
|
|
|||
337
src/desktop/view/Group.cpp
Normal file
337
src/desktop/view/Group.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#include "Group.hpp"
|
||||
#include "Window.hpp"
|
||||
|
||||
#include "../../render/decorations/CHyprGroupBarDecoration.hpp"
|
||||
#include "../../layout/target/WindowGroupTarget.hpp"
|
||||
#include "../../layout/target/WindowTarget.hpp"
|
||||
#include "../../layout/target/Target.hpp"
|
||||
#include "../../layout/space/Space.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
#include "../../desktop/state/FocusState.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Desktop;
|
||||
using namespace Desktop::View;
|
||||
|
||||
std::vector<WP<CGroup>>& View::groups() {
|
||||
static std::vector<WP<CGroup>> g;
|
||||
return g;
|
||||
}
|
||||
|
||||
SP<CGroup> CGroup::create(std::vector<PHLWINDOWREF>&& windows) {
|
||||
auto x = SP<CGroup>(new CGroup(std::move(windows)));
|
||||
x->m_self = x;
|
||||
x->m_target = Layout::CWindowGroupTarget::create(x);
|
||||
groups().emplace_back(x);
|
||||
|
||||
x->init();
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
CGroup::CGroup(std::vector<PHLWINDOWREF>&& windows) : m_windows(std::move(windows)) {
|
||||
;
|
||||
}
|
||||
|
||||
void CGroup::init() {
|
||||
// for proper group logic:
|
||||
// - add all windows to us
|
||||
// - replace the first window with our target
|
||||
// - remove all window targets from layout
|
||||
// - apply updates
|
||||
|
||||
// FIXME: what if some windows are grouped? For now we only do 1-window but YNK
|
||||
for (const auto& w : m_windows) {
|
||||
RASSERT(!w->m_group, "CGroup: windows cannot contain grouped in init, this will explode");
|
||||
w->m_group = m_self.lock();
|
||||
}
|
||||
|
||||
g_layoutManager->switchTargets(m_windows.at(0)->m_target, m_target);
|
||||
|
||||
for (const auto& w : m_windows) {
|
||||
w->m_target->setSpaceGhost(m_target->space());
|
||||
}
|
||||
|
||||
for (const auto& w : m_windows) {
|
||||
applyWindowDecosAndUpdates(w.lock());
|
||||
}
|
||||
|
||||
updateWindowVisibility();
|
||||
}
|
||||
|
||||
void CGroup::destroy() {
|
||||
while (true) {
|
||||
if (m_windows.size() == 1) {
|
||||
remove(m_windows.at(0).lock());
|
||||
break;
|
||||
}
|
||||
|
||||
remove(m_windows.at(0).lock());
|
||||
}
|
||||
}
|
||||
|
||||
CGroup::~CGroup() {
|
||||
if (m_target->space())
|
||||
m_target->assignToSpace(nullptr);
|
||||
std::erase_if(groups(), [this](const auto& e) { return !e || e == m_self; });
|
||||
}
|
||||
|
||||
bool CGroup::has(PHLWINDOW w) const {
|
||||
return std::ranges::contains(m_windows, w);
|
||||
}
|
||||
|
||||
void CGroup::add(PHLWINDOW w) {
|
||||
static auto INSERT_AFTER_CURRENT = CConfigValue<Hyprlang::INT>("group:insert_after_current");
|
||||
|
||||
if (w->m_group) {
|
||||
if (w->m_group == m_self)
|
||||
return;
|
||||
|
||||
const auto WINDOWS = w->m_group->windows();
|
||||
for (const auto& w : WINDOWS) {
|
||||
w->m_group->remove(w.lock());
|
||||
add(w.lock());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (w->layoutTarget()->space()) {
|
||||
// remove the target from a space if it is in one
|
||||
g_layoutManager->removeTarget(w->layoutTarget());
|
||||
}
|
||||
|
||||
w->m_group = m_self.lock();
|
||||
w->m_target->setSpaceGhost(m_target->space());
|
||||
w->m_target->setFloating(m_target->floating());
|
||||
|
||||
if (*INSERT_AFTER_CURRENT) {
|
||||
m_windows.insert(m_windows.begin() + m_current + 1, w);
|
||||
m_current++;
|
||||
} else {
|
||||
m_windows.emplace_back(w);
|
||||
m_current = m_windows.size() - 1;
|
||||
}
|
||||
|
||||
applyWindowDecosAndUpdates(w);
|
||||
updateWindowVisibility();
|
||||
m_target->recalc();
|
||||
}
|
||||
|
||||
void CGroup::remove(PHLWINDOW w) {
|
||||
std::optional<size_t> idx;
|
||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||
if (m_windows.at(i) == w) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!idx)
|
||||
return;
|
||||
|
||||
if ((m_current >= *idx && idx != 0) || (m_current >= m_windows.size() - 1 && m_current > 0))
|
||||
m_current--;
|
||||
|
||||
auto g = m_self.lock(); // keep ref to avoid uaf after w->m_group.reset()
|
||||
|
||||
w->m_group.reset();
|
||||
removeWindowDecos(w);
|
||||
|
||||
w->setHidden(false);
|
||||
|
||||
const bool REMOVING_GROUP = m_windows.size() <= 1;
|
||||
|
||||
if (REMOVING_GROUP) {
|
||||
w->m_target->assignToSpace(nullptr);
|
||||
g_layoutManager->switchTargets(m_target, w->m_target);
|
||||
}
|
||||
|
||||
// we do it after the above because switchTargets expects this to be a valid group
|
||||
m_windows.erase(m_windows.begin() + *idx);
|
||||
|
||||
if (!m_windows.empty())
|
||||
updateWindowVisibility();
|
||||
|
||||
// do this here: otherwise the new current is hidden and workspace rules get wrong data
|
||||
if (!REMOVING_GROUP)
|
||||
w->m_target->assignToSpace(m_target->space());
|
||||
}
|
||||
|
||||
void CGroup::moveCurrent(bool next) {
|
||||
size_t idx = m_current;
|
||||
|
||||
if (next) {
|
||||
idx++;
|
||||
if (idx >= m_windows.size())
|
||||
idx = 0;
|
||||
} else {
|
||||
if (idx == 0)
|
||||
idx = m_windows.size() - 1;
|
||||
else
|
||||
idx--;
|
||||
}
|
||||
|
||||
setCurrent(idx);
|
||||
}
|
||||
|
||||
void CGroup::setCurrent(size_t idx) {
|
||||
if (idx == m_current)
|
||||
return;
|
||||
|
||||
const auto FS_STATE = m_target->fullscreenMode();
|
||||
const auto WASFOCUS = Desktop::focusState()->window() == current();
|
||||
auto oldWindow = m_windows.at(m_current).lock();
|
||||
|
||||
if (FS_STATE != FSMODE_NONE)
|
||||
g_pCompositor->setWindowFullscreenInternal(oldWindow, FSMODE_NONE);
|
||||
|
||||
m_current = std::clamp(idx, sc<size_t>(0), m_windows.size() - 1);
|
||||
updateWindowVisibility();
|
||||
|
||||
auto newWindow = m_windows.at(m_current).lock();
|
||||
|
||||
if (FS_STATE != FSMODE_NONE) {
|
||||
g_pCompositor->setWindowFullscreenInternal(newWindow, FS_STATE);
|
||||
newWindow->m_target->warpPositionSize();
|
||||
oldWindow->m_target->setPositionGlobal(newWindow->m_target->position()); // TODO: this is a hack and sucks
|
||||
}
|
||||
|
||||
if (WASFOCUS)
|
||||
Desktop::focusState()->rawWindowFocus(current(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
}
|
||||
|
||||
void CGroup::setCurrent(PHLWINDOW w) {
|
||||
if (w == current())
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||
if (m_windows.at(i) == w) {
|
||||
setCurrent(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t CGroup::getCurrentIdx() const {
|
||||
return m_current;
|
||||
}
|
||||
|
||||
PHLWINDOW CGroup::head() const {
|
||||
return m_windows.front().lock();
|
||||
}
|
||||
|
||||
PHLWINDOW CGroup::tail() const {
|
||||
return m_windows.back().lock();
|
||||
}
|
||||
|
||||
PHLWINDOW CGroup::current() const {
|
||||
return m_windows.at(m_current).lock();
|
||||
}
|
||||
|
||||
PHLWINDOW CGroup::next() const {
|
||||
return (m_current >= m_windows.size() - 1 ? m_windows.front() : m_windows.at(m_current + 1)).lock();
|
||||
}
|
||||
|
||||
PHLWINDOW CGroup::fromIndex(size_t idx) const {
|
||||
if (idx >= m_windows.size())
|
||||
return nullptr;
|
||||
|
||||
return m_windows.at(idx).lock();
|
||||
}
|
||||
|
||||
const std::vector<PHLWINDOWREF>& CGroup::windows() const {
|
||||
return m_windows;
|
||||
}
|
||||
|
||||
void CGroup::applyWindowDecosAndUpdates(PHLWINDOW x) {
|
||||
x->addWindowDeco(makeUnique<CHyprGroupBarDecoration>(x));
|
||||
|
||||
x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
x->updateWindowDecos();
|
||||
x->updateDecorationValues();
|
||||
}
|
||||
|
||||
void CGroup::removeWindowDecos(PHLWINDOW x) {
|
||||
x->removeWindowDeco(x->getDecorationByType(DECORATION_GROUPBAR));
|
||||
|
||||
x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
x->updateWindowDecos();
|
||||
x->updateDecorationValues();
|
||||
}
|
||||
|
||||
void CGroup::updateWindowVisibility() {
|
||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||
if (i == m_current) {
|
||||
auto& x = m_windows.at(i);
|
||||
x->setHidden(false);
|
||||
x->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
x->updateWindowDecos();
|
||||
x->updateDecorationValues();
|
||||
} else
|
||||
m_windows.at(i)->setHidden(true);
|
||||
}
|
||||
|
||||
m_target->recalc();
|
||||
|
||||
m_target->damageEntire();
|
||||
}
|
||||
|
||||
size_t CGroup::size() const {
|
||||
return m_windows.size();
|
||||
}
|
||||
|
||||
bool CGroup::locked() const {
|
||||
return m_locked;
|
||||
}
|
||||
|
||||
void CGroup::setLocked(bool x) {
|
||||
m_locked = x;
|
||||
}
|
||||
|
||||
bool CGroup::denied() const {
|
||||
return m_deny;
|
||||
}
|
||||
|
||||
void CGroup::setDenied(bool x) {
|
||||
m_deny = x;
|
||||
}
|
||||
|
||||
void CGroup::updateWorkspace(PHLWORKSPACE ws) {
|
||||
if (!ws)
|
||||
return;
|
||||
|
||||
for (const auto& w : windows()) {
|
||||
w->m_monitor = ws->m_monitor;
|
||||
w->moveToWorkspace(ws);
|
||||
w->updateToplevel();
|
||||
w->updateWindowDecos();
|
||||
w->m_target->setSpaceGhost(ws->m_space);
|
||||
}
|
||||
}
|
||||
|
||||
void CGroup::swapWithNext() {
|
||||
const bool HAD_FOCUS = Desktop::focusState()->window() == m_windows.at(m_current);
|
||||
|
||||
size_t idx = m_current + 1 >= m_windows.size() ? 0 : m_current + 1;
|
||||
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
||||
|
||||
updateWindowVisibility();
|
||||
|
||||
if (HAD_FOCUS)
|
||||
Desktop::focusState()->fullWindowFocus(m_windows.at(m_current).lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
}
|
||||
|
||||
void CGroup::swapWithLast() {
|
||||
const bool HAD_FOCUS = Desktop::focusState()->window() == m_windows.at(m_current);
|
||||
|
||||
size_t idx = m_current == 0 ? m_windows.size() - 1 : m_current - 1;
|
||||
std::iter_swap(m_windows.begin() + m_current, m_windows.begin() + idx);
|
||||
|
||||
updateWindowVisibility();
|
||||
|
||||
if (HAD_FOCUS)
|
||||
Desktop::focusState()->fullWindowFocus(m_windows.at(m_current).lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
}
|
||||
68
src/desktop/view/Group.hpp
Normal file
68
src/desktop/view/Group.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DesktopTypes.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Layout {
|
||||
class CWindowGroupTarget;
|
||||
};
|
||||
|
||||
namespace Desktop::View {
|
||||
class CGroup {
|
||||
public:
|
||||
static SP<CGroup> create(std::vector<PHLWINDOWREF>&& windows);
|
||||
~CGroup();
|
||||
|
||||
bool has(PHLWINDOW w) const;
|
||||
|
||||
void add(PHLWINDOW w);
|
||||
void remove(PHLWINDOW w);
|
||||
void moveCurrent(bool next);
|
||||
void setCurrent(size_t idx);
|
||||
void setCurrent(PHLWINDOW w);
|
||||
size_t getCurrentIdx() const;
|
||||
size_t size() const;
|
||||
void destroy();
|
||||
void updateWorkspace(PHLWORKSPACE);
|
||||
|
||||
void swapWithNext();
|
||||
void swapWithLast();
|
||||
|
||||
PHLWINDOW head() const;
|
||||
PHLWINDOW tail() const;
|
||||
PHLWINDOW current() const;
|
||||
PHLWINDOW next() const;
|
||||
|
||||
PHLWINDOW fromIndex(size_t idx) const;
|
||||
|
||||
bool locked() const;
|
||||
void setLocked(bool x);
|
||||
|
||||
bool denied() const;
|
||||
void setDenied(bool x);
|
||||
|
||||
const std::vector<PHLWINDOWREF>& windows() const;
|
||||
|
||||
SP<Layout::CWindowGroupTarget> m_target;
|
||||
|
||||
private:
|
||||
CGroup(std::vector<PHLWINDOWREF>&& windows);
|
||||
|
||||
void applyWindowDecosAndUpdates(PHLWINDOW x);
|
||||
void removeWindowDecos(PHLWINDOW x);
|
||||
void init();
|
||||
void updateWindowVisibility();
|
||||
|
||||
WP<CGroup> m_self;
|
||||
|
||||
std::vector<PHLWINDOWREF> m_windows;
|
||||
|
||||
bool m_locked = false;
|
||||
bool m_deny = false;
|
||||
|
||||
size_t m_current = 0;
|
||||
};
|
||||
|
||||
std::vector<WP<CGroup>>& groups();
|
||||
};
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
#include <hyprutils/animation/AnimatedVariable.hpp>
|
||||
#include <re2/re2.h>
|
||||
|
||||
#include "Group.hpp"
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
|
@ -37,12 +39,15 @@
|
|||
#include "../../helpers/math/Expression.hpp"
|
||||
#include "../../managers/XWaylandManager.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
#include "../../managers/LayoutManager.hpp"
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../managers/EventManager.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../managers/PointerManager.hpp"
|
||||
#include "../../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../layout/space/Space.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
#include "../../layout/target/WindowTarget.hpp"
|
||||
#include "../../layout/target/WindowGroupTarget.hpp"
|
||||
|
||||
#include <hyprutils/string/String.hpp>
|
||||
|
||||
|
|
@ -57,6 +62,9 @@ using namespace Desktop::View;
|
|||
static uint64_t windowIDCounter = 0x18000000;
|
||||
|
||||
//
|
||||
#define COMMA ,
|
||||
//
|
||||
|
||||
PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
|
||||
PHLWINDOW pWindow = SP<CWindow>(new CWindow(surface));
|
||||
|
||||
|
|
@ -79,6 +87,8 @@ PHLWINDOW CWindow::create(SP<CXWaylandSurface> surface) {
|
|||
pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow));
|
||||
pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow));
|
||||
|
||||
pWindow->m_target = Layout::CWindowTarget::create(pWindow);
|
||||
|
||||
return pWindow;
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +114,8 @@ PHLWINDOW CWindow::create(SP<CXDGSurfaceResource> resource) {
|
|||
pWindow->addWindowDeco(makeUnique<CHyprDropShadowDecoration>(pWindow));
|
||||
pWindow->addWindowDeco(makeUnique<CHyprBorderDecoration>(pWindow));
|
||||
|
||||
pWindow->m_target = Layout::CWindowTarget::create(pWindow);
|
||||
|
||||
pWindow->wlSurface()->assign(pWindow->m_xdgSurface->m_surface.lock(), pWindow);
|
||||
|
||||
return pWindow;
|
||||
|
|
@ -263,23 +275,24 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() {
|
|||
return CBox{sc<int>(POS.x), sc<int>(POS.y), sc<int>(SIZE.x), sc<int>(SIZE.y)};
|
||||
}
|
||||
|
||||
// get work area
|
||||
const auto WORKAREA = g_pLayoutManager->getCurrentLayout()->workAreaOnWorkspace(m_workspace);
|
||||
const auto RESERVED = CReservedArea{PMONITOR->logicalBox(), WORKAREA};
|
||||
// fucker fucking fuck
|
||||
const auto WORKAREA = m_workspace->m_space->workArea();
|
||||
const auto& RESERVED = PMONITOR->m_reservedArea;
|
||||
|
||||
if (DELTALESSTHAN(POS.y - PMONITOR->m_position.y, RESERVED.top(), 1)) {
|
||||
POS.y = PMONITOR->m_position.y;
|
||||
SIZE.y += RESERVED.top();
|
||||
}
|
||||
if (DELTALESSTHAN(POS.x - PMONITOR->m_position.x, RESERVED.left(), 1)) {
|
||||
POS.x = PMONITOR->m_position.x;
|
||||
if (DELTALESSTHAN(POS.x, WORKAREA.x, 1)) {
|
||||
POS.x -= RESERVED.left();
|
||||
SIZE.x += RESERVED.left();
|
||||
}
|
||||
|
||||
if (DELTALESSTHAN(POS.x + SIZE.x - PMONITOR->m_position.x, PMONITOR->m_size.x - RESERVED.right(), 1))
|
||||
if (DELTALESSTHAN(POS.y, WORKAREA.y, 1)) {
|
||||
POS.y -= RESERVED.top();
|
||||
SIZE.y += RESERVED.top();
|
||||
}
|
||||
|
||||
if (DELTALESSTHAN(POS.x + SIZE.x, WORKAREA.x + WORKAREA.width, 1))
|
||||
SIZE.x += RESERVED.right();
|
||||
|
||||
if (DELTALESSTHAN(POS.y + SIZE.y - PMONITOR->m_position.y, PMONITOR->m_size.y - RESERVED.bottom(), 1))
|
||||
if (DELTALESSTHAN(POS.y + SIZE.y, WORKAREA.y + WORKAREA.height, 1))
|
||||
SIZE.y += RESERVED.bottom();
|
||||
|
||||
return CBox{sc<int>(POS.x), sc<int>(POS.y), sc<int>(SIZE.x), sc<int>(SIZE.y)};
|
||||
|
|
@ -357,14 +370,18 @@ void CWindow::addWindowDeco(UP<IHyprWindowDecoration> deco) {
|
|||
m_windowDecorations.emplace_back(std::move(deco));
|
||||
g_pDecorationPositioner->forceRecalcFor(m_self.lock());
|
||||
updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_self.lock());
|
||||
|
||||
if (layoutTarget())
|
||||
layoutTarget()->recalc();
|
||||
}
|
||||
|
||||
void CWindow::removeWindowDeco(IHyprWindowDecoration* deco) {
|
||||
m_decosToRemove.push_back(deco);
|
||||
g_pDecorationPositioner->forceRecalcFor(m_self.lock());
|
||||
updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_self.lock());
|
||||
|
||||
if (layoutTarget())
|
||||
layoutTarget()->recalc();
|
||||
}
|
||||
|
||||
void CWindow::uncacheWindowDecos() {
|
||||
|
|
@ -495,11 +512,9 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) {
|
|||
|
||||
OLDWORKSPACE->updateWindows();
|
||||
OLDWORKSPACE->updateWindowData();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(OLDWORKSPACE->monitorID());
|
||||
|
||||
pWorkspace->updateWindows();
|
||||
pWorkspace->updateWindowData();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
|
|
@ -591,7 +606,7 @@ void CWindow::onUnmap() {
|
|||
m_workspace->updateWindows();
|
||||
m_workspace->updateWindowData();
|
||||
}
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
m_workspace.reset();
|
||||
|
|
@ -723,311 +738,6 @@ bool CWindow::hasPopupAt(const Vector2D& pos) {
|
|||
return popup && popup->wlSurface()->resource();
|
||||
}
|
||||
|
||||
void CWindow::applyGroupRules() {
|
||||
if ((m_groupRules & GROUP_SET && m_firstMap) || m_groupRules & GROUP_SET_ALWAYS)
|
||||
createGroup();
|
||||
|
||||
if (m_groupData.pNextWindow.lock() && ((m_groupRules & GROUP_LOCK && m_firstMap) || m_groupRules & GROUP_LOCK_ALWAYS))
|
||||
getGroupHead()->m_groupData.locked = true;
|
||||
}
|
||||
|
||||
void CWindow::createGroup() {
|
||||
if (m_groupData.deny) {
|
||||
Log::logger->log(Log::DEBUG, "createGroup: window:{:x},title:{} is denied as a group, ignored", rc<uintptr_t>(this), this->m_title);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_groupData.pNextWindow.expired()) {
|
||||
m_groupData.pNextWindow = m_self;
|
||||
m_groupData.head = true;
|
||||
m_groupData.locked = false;
|
||||
m_groupData.deny = false;
|
||||
|
||||
addWindowDeco(makeUnique<CHyprGroupBarDecoration>(m_self.lock()));
|
||||
|
||||
if (m_workspace) {
|
||||
m_workspace->updateWindows();
|
||||
m_workspace->updateWindowData();
|
||||
}
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("1,{:x}", rc<uintptr_t>(this))});
|
||||
}
|
||||
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
}
|
||||
|
||||
void CWindow::destroyGroup() {
|
||||
if (m_groupData.pNextWindow == m_self) {
|
||||
if (m_groupRules & GROUP_SET_ALWAYS) {
|
||||
Log::logger->log(Log::DEBUG, "destoryGroup: window:{:x},title:{} has rule [group set always], ignored", rc<uintptr_t>(this), this->m_title);
|
||||
return;
|
||||
}
|
||||
m_groupData.pNextWindow.reset();
|
||||
m_groupData.head = false;
|
||||
updateWindowDecos();
|
||||
if (m_workspace) {
|
||||
m_workspace->updateWindows();
|
||||
m_workspace->updateWindowData();
|
||||
}
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{:x}", rc<uintptr_t>(this))});
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string addresses;
|
||||
PHLWINDOW curr = m_self.lock();
|
||||
std::vector<PHLWINDOW> members;
|
||||
do {
|
||||
const auto PLASTWIN = curr;
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
PLASTWIN->m_groupData.pNextWindow.reset();
|
||||
curr->setHidden(false);
|
||||
members.push_back(curr);
|
||||
|
||||
addresses += std::format("{:x},", rc<uintptr_t>(curr.get()));
|
||||
} while (curr.get() != this);
|
||||
|
||||
for (auto const& w : members) {
|
||||
if (w->m_groupData.head)
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(curr);
|
||||
w->m_groupData.head = false;
|
||||
}
|
||||
|
||||
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_groupsLocked;
|
||||
g_pKeybindManager->m_groupsLocked = true;
|
||||
for (auto const& w : members) {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(w);
|
||||
w->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
w->updateWindowDecos();
|
||||
}
|
||||
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
|
||||
|
||||
if (m_workspace) {
|
||||
m_workspace->updateWindows();
|
||||
m_workspace->updateWindowData();
|
||||
}
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
if (!addresses.empty())
|
||||
addresses.pop_back();
|
||||
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "togglegroup", .data = std::format("0,{}", addresses)});
|
||||
}
|
||||
|
||||
PHLWINDOW CWindow::getGroupHead() {
|
||||
PHLWINDOW curr = m_self.lock();
|
||||
while (!curr->m_groupData.head)
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
return curr;
|
||||
}
|
||||
|
||||
PHLWINDOW CWindow::getGroupTail() {
|
||||
PHLWINDOW curr = m_self.lock();
|
||||
while (!curr->m_groupData.pNextWindow->m_groupData.head)
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
return curr;
|
||||
}
|
||||
|
||||
PHLWINDOW CWindow::getGroupCurrent() {
|
||||
PHLWINDOW curr = m_self.lock();
|
||||
while (curr->isHidden())
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
return curr;
|
||||
}
|
||||
|
||||
int CWindow::getGroupSize() {
|
||||
int size = 1;
|
||||
PHLWINDOW curr = m_self.lock();
|
||||
while (curr->m_groupData.pNextWindow != m_self) {
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool CWindow::canBeGroupedInto(PHLWINDOW pWindow) {
|
||||
static auto ALLOWGROUPMERGE = CConfigValue<Hyprlang::INT>("group:merge_groups_on_drag");
|
||||
bool isGroup = m_groupData.pNextWindow;
|
||||
bool disallowDragIntoGroup = g_pInputManager->m_wasDraggingWindow && isGroup && !sc<bool>(*ALLOWGROUPMERGE);
|
||||
return !g_pKeybindManager->m_groupsLocked // global group lock disengaged
|
||||
&& ((m_groupRules & GROUP_INVADE && m_firstMap) // window ignore local group locks, or
|
||||
|| (!pWindow->getGroupHead()->m_groupData.locked // target unlocked
|
||||
&& !(m_groupData.pNextWindow.lock() && getGroupHead()->m_groupData.locked))) // source unlocked or isn't group
|
||||
&& !m_groupData.deny // source is not denied entry
|
||||
&& !(m_groupRules & GROUP_BARRED && m_firstMap) // group rule doesn't prevent adding window
|
||||
&& !disallowDragIntoGroup; // config allows groups to be merged
|
||||
}
|
||||
|
||||
PHLWINDOW CWindow::getGroupWindowByIndex(int index) {
|
||||
const int SIZE = getGroupSize();
|
||||
index = ((index % SIZE) + SIZE) % SIZE;
|
||||
PHLWINDOW curr = getGroupHead();
|
||||
while (index > 0) {
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
index--;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
bool CWindow::hasInGroup(PHLWINDOW w) {
|
||||
PHLWINDOW curr = m_groupData.pNextWindow.lock();
|
||||
while (curr && curr != m_self) {
|
||||
if (curr == w)
|
||||
return true;
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CWindow::setGroupCurrent(PHLWINDOW pWindow) {
|
||||
PHLWINDOW curr = m_groupData.pNextWindow.lock();
|
||||
bool isMember = false;
|
||||
while (curr.get() != this) {
|
||||
if (curr == pWindow) {
|
||||
isMember = true;
|
||||
break;
|
||||
}
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
|
||||
if (!isMember && pWindow.get() != this)
|
||||
return;
|
||||
|
||||
const auto PCURRENT = getGroupCurrent();
|
||||
const bool FULLSCREEN = PCURRENT->isFullscreen();
|
||||
const auto WORKSPACE = PCURRENT->m_workspace;
|
||||
const auto MODE = PCURRENT->m_fullscreenState.internal;
|
||||
|
||||
const auto CURRENTISFOCUS = PCURRENT == Desktop::focusState()->window();
|
||||
|
||||
const auto PWINDOWSIZE = PCURRENT->m_realSize->value();
|
||||
const auto PWINDOWPOS = PCURRENT->m_realPosition->value();
|
||||
const auto PWINDOWSIZEGOAL = PCURRENT->m_realSize->goal();
|
||||
const auto PWINDOWPOSGOAL = PCURRENT->m_realPosition->goal();
|
||||
const auto PWINDOWLASTFLOATINGSIZE = PCURRENT->m_lastFloatingSize;
|
||||
const auto PWINDOWLASTFLOATINGPOSITION = PCURRENT->m_lastFloatingPosition;
|
||||
|
||||
if (FULLSCREEN)
|
||||
g_pCompositor->setWindowFullscreenInternal(PCURRENT, FSMODE_NONE);
|
||||
|
||||
PCURRENT->setHidden(true);
|
||||
pWindow->setHidden(false); // can remove m_pLastWindow
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->replaceWindowDataWith(PCURRENT, pWindow);
|
||||
|
||||
if (PCURRENT->m_isFloating) {
|
||||
pWindow->m_realPosition->setValueAndWarp(PWINDOWPOSGOAL);
|
||||
pWindow->m_realSize->setValueAndWarp(PWINDOWSIZEGOAL);
|
||||
pWindow->sendWindowSize();
|
||||
}
|
||||
|
||||
pWindow->m_realPosition->setValue(PWINDOWPOS);
|
||||
pWindow->m_realSize->setValue(PWINDOWSIZE);
|
||||
|
||||
if (FULLSCREEN)
|
||||
g_pCompositor->setWindowFullscreenInternal(pWindow, MODE);
|
||||
|
||||
pWindow->m_lastFloatingSize = PWINDOWLASTFLOATINGSIZE;
|
||||
pWindow->m_lastFloatingPosition = PWINDOWLASTFLOATINGPOSITION;
|
||||
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
if (CURRENTISFOCUS)
|
||||
Desktop::focusState()->rawWindowFocus(pWindow);
|
||||
|
||||
g_pHyprRenderer->damageWindow(pWindow);
|
||||
|
||||
pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
pWindow->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindow::insertWindowToGroup(PHLWINDOW pWindow) {
|
||||
const auto BEGINAT = m_self.lock();
|
||||
const auto ENDAT = m_groupData.pNextWindow.lock();
|
||||
|
||||
if (!pWindow->m_groupData.pNextWindow.lock()) {
|
||||
BEGINAT->m_groupData.pNextWindow = pWindow;
|
||||
pWindow->m_groupData.pNextWindow = ENDAT;
|
||||
pWindow->m_groupData.head = false;
|
||||
pWindow->addWindowDeco(makeUnique<CHyprGroupBarDecoration>(pWindow));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto SHEAD = pWindow->getGroupHead();
|
||||
const auto STAIL = pWindow->getGroupTail();
|
||||
|
||||
SHEAD->m_groupData.head = false;
|
||||
BEGINAT->m_groupData.pNextWindow = SHEAD;
|
||||
STAIL->m_groupData.pNextWindow = ENDAT;
|
||||
|
||||
pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
pWindow->updateWindowDecos();
|
||||
}
|
||||
|
||||
PHLWINDOW CWindow::getGroupPrevious() {
|
||||
PHLWINDOW curr = m_groupData.pNextWindow.lock();
|
||||
|
||||
while (curr != m_self && curr->m_groupData.pNextWindow != m_self)
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
void CWindow::switchWithWindowInGroup(PHLWINDOW pWindow) {
|
||||
if (!m_groupData.pNextWindow.lock() || !pWindow->m_groupData.pNextWindow.lock())
|
||||
return;
|
||||
|
||||
if (m_groupData.pNextWindow.lock() == pWindow) { // A -> this -> pWindow -> B >> A -> pWindow -> this -> B
|
||||
getGroupPrevious()->m_groupData.pNextWindow = pWindow;
|
||||
m_groupData.pNextWindow = pWindow->m_groupData.pNextWindow;
|
||||
pWindow->m_groupData.pNextWindow = m_self;
|
||||
|
||||
} else if (pWindow->m_groupData.pNextWindow == m_self) { // A -> pWindow -> this -> B >> A -> this -> pWindow -> B
|
||||
pWindow->getGroupPrevious()->m_groupData.pNextWindow = m_self;
|
||||
pWindow->m_groupData.pNextWindow = m_groupData.pNextWindow;
|
||||
m_groupData.pNextWindow = pWindow;
|
||||
|
||||
} else { // A -> this -> B | C -> pWindow -> D >> A -> pWindow -> B | C -> this -> D
|
||||
std::swap(m_groupData.pNextWindow, pWindow->m_groupData.pNextWindow);
|
||||
std::swap(getGroupPrevious()->m_groupData.pNextWindow, pWindow->getGroupPrevious()->m_groupData.pNextWindow);
|
||||
}
|
||||
|
||||
std::swap(m_groupData.head, pWindow->m_groupData.head);
|
||||
std::swap(m_groupData.locked, pWindow->m_groupData.locked);
|
||||
|
||||
pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_GROUP | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
|
||||
pWindow->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindow::updateGroupOutputs() {
|
||||
if (m_groupData.pNextWindow.expired())
|
||||
return;
|
||||
|
||||
PHLWINDOW curr = m_groupData.pNextWindow.lock();
|
||||
|
||||
const auto WS = m_workspace;
|
||||
|
||||
while (curr.get() != this) {
|
||||
curr->m_monitor = m_monitor;
|
||||
curr->moveToWorkspace(WS);
|
||||
|
||||
*curr->m_realPosition = m_realPosition->goal();
|
||||
*curr->m_realSize = m_realSize->goal();
|
||||
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2D CWindow::middle() {
|
||||
return m_realPosition->goal() + m_realSize->goal() / 2.f;
|
||||
}
|
||||
|
|
@ -1148,7 +858,7 @@ void CWindow::setAnimationsToMove() {
|
|||
|
||||
void CWindow::onWorkspaceAnimUpdate() {
|
||||
// clip box for animated offsets
|
||||
if (!m_isFloating || m_pinned || isFullscreen() || m_draggingTiled) {
|
||||
if (!m_isFloating || m_pinned || isFullscreen()) {
|
||||
m_floatingOffset = Vector2D(0, 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1326,7 +1036,7 @@ void CWindow::activate(bool force) {
|
|||
if (m_isFloating)
|
||||
g_pCompositor->changeWindowZOrder(m_self.lock(), true);
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock());
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
warpCursor();
|
||||
}
|
||||
|
||||
|
|
@ -1378,7 +1088,8 @@ void CWindow::onUpdateMeta() {
|
|||
if (m_self == Desktop::focusState()->window()) { // if it's the active, let's post an event to update others
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = m_class + "," + m_title});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc<uintptr_t>(this))});
|
||||
EMIT_HOOK_EVENT("activeWindow", m_self.lock());
|
||||
|
||||
// no need for a hook event
|
||||
}
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Window {:x} set title to {}", rc<uintptr_t>(this), m_title);
|
||||
|
|
@ -1392,7 +1103,8 @@ void CWindow::onUpdateMeta() {
|
|||
if (m_self == Desktop::focusState()->window()) { // if it's the active, let's post an event to update others
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindow", .data = m_class + "," + m_title});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{.event = "activewindowv2", .data = std::format("{:x}", rc<uintptr_t>(this))});
|
||||
EMIT_HOOK_EVENT("activeWindow", m_self.lock());
|
||||
|
||||
// no need for a hook event
|
||||
}
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Window {:x} set class to {}", rc<uintptr_t>(this), m_class);
|
||||
|
|
@ -1472,7 +1184,7 @@ void CWindow::onX11ConfigureRequest(CBox box) {
|
|||
|
||||
g_pHyprRenderer->damageWindow(m_self.lock());
|
||||
|
||||
if (!m_isFloating || isFullscreen() || g_pInputManager->m_currentlyDraggedWindow == m_self) {
|
||||
if (!m_isFloating || isFullscreen() || g_layoutManager->dragController()->target() == m_self) {
|
||||
sendWindowSize(true);
|
||||
g_pInputManager->refocus();
|
||||
g_pHyprRenderer->damageWindow(m_self.lock());
|
||||
|
|
@ -1689,20 +1401,17 @@ void CWindow::setContentType(NContentType::eContentType contentType) {
|
|||
}
|
||||
|
||||
void CWindow::deactivateGroupMembers() {
|
||||
auto curr = getGroupHead();
|
||||
while (curr) {
|
||||
if (curr != m_self.lock()) {
|
||||
if (!m_group)
|
||||
return;
|
||||
for (const auto& w : m_group->windows()) {
|
||||
if (w != m_self.lock()) {
|
||||
// we don't want to deactivate unfocused xwayland windows
|
||||
// because X is weird, keep the behavior for wayland windows
|
||||
// also its not really needed for xwayland windows
|
||||
// ref: #9760 #9294
|
||||
if (!curr->m_isX11 && curr->m_xdgSurface && curr->m_xdgSurface->m_toplevel)
|
||||
curr->m_xdgSurface->m_toplevel->setActive(false);
|
||||
if (!w->m_isX11 && w->m_xdgSurface && w->m_xdgSurface->m_toplevel)
|
||||
w->m_xdgSurface->m_toplevel->setActive(false);
|
||||
}
|
||||
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
if (curr == getGroupHead())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1827,21 +1536,13 @@ void CWindow::updateDecorationValues() {
|
|||
|
||||
const bool IS_SHADOWED_BY_MODAL = m_xdgSurface && m_xdgSurface->m_toplevel && m_xdgSurface->m_toplevel->anyChildModal();
|
||||
|
||||
// border
|
||||
const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(m_self.lock());
|
||||
if (RENDERDATA.isBorderGradient)
|
||||
setBorderColor(*RENDERDATA.borderGradient);
|
||||
else {
|
||||
const bool GROUPLOCKED = m_groupData.pNextWindow.lock() ? getGroupHead()->m_groupData.locked : false;
|
||||
if (m_self == Desktop::focusState()->window()) {
|
||||
const auto* const ACTIVECOLOR =
|
||||
!m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
||||
setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR));
|
||||
} else {
|
||||
const auto* const INACTIVECOLOR =
|
||||
!m_groupData.pNextWindow.lock() ? (!m_groupData.deny ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
||||
setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR));
|
||||
}
|
||||
const bool GROUPLOCKED = m_group ? m_group->locked() : false;
|
||||
if (m_self == Desktop::focusState()->window()) {
|
||||
const auto* const ACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? ACTIVECOL : NOGROUPACTIVECOL) : (GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL);
|
||||
setBorderColor(m_ruleApplicator->activeBorderColor().valueOr(*ACTIVECOLOR));
|
||||
} else {
|
||||
const auto* const INACTIVECOLOR = !m_group ? (!(m_groupRules & GROUP_DENY) ? INACTIVECOL : NOGROUPINACTIVECOL) : (GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL);
|
||||
setBorderColor(m_ruleApplicator->inactiveBorderColor().valueOr(*INACTIVECOLOR));
|
||||
}
|
||||
|
||||
// opacity
|
||||
|
|
@ -1929,6 +1630,7 @@ void CWindow::mapWindow() {
|
|||
static auto PDIMSTRENGTH = CConfigValue<Hyprlang::FLOAT>("decoration:dim_strength");
|
||||
static auto PNEWTAKESOVERFS = CConfigValue<Hyprlang::INT>("misc:on_focus_under_fullscreen");
|
||||
static auto PINITIALWSTRACKING = CConfigValue<Hyprlang::INT>("misc:initial_workspace_tracking");
|
||||
static auto PAUTOGROUP = CConfigValue<Hyprlang::INT>("group:auto_group");
|
||||
|
||||
const auto LAST_FOCUS_WINDOW = Desktop::focusState()->window();
|
||||
const bool IS_LAST_IN_FS = LAST_FOCUS_WINDOW ? LAST_FOCUS_WINDOW->m_fullscreenState.internal != FSMODE_NONE : false;
|
||||
|
|
@ -2053,8 +1755,8 @@ void CWindow::mapWindow() {
|
|||
requestedFSMonitor = MONITOR_INVALID;
|
||||
}
|
||||
|
||||
m_isFloating = m_ruleApplicator->static_.floating.value_or(m_isFloating);
|
||||
m_isPseudotiled = m_ruleApplicator->static_.pseudo.value_or(m_isPseudotiled);
|
||||
m_isFloating = m_ruleApplicator->static_.floating.value_or(m_isFloating);
|
||||
m_target->setPseudo(m_ruleApplicator->static_.pseudo.value_or(m_target->isPseudo()));
|
||||
m_noInitialFocus = m_ruleApplicator->static_.noInitialFocus.value_or(m_noInitialFocus);
|
||||
m_pinned = m_ruleApplicator->static_.pin.value_or(m_pinned);
|
||||
|
||||
|
|
@ -2109,7 +1811,7 @@ void CWindow::mapWindow() {
|
|||
} else if (v == "barred") {
|
||||
m_groupRules |= Desktop::View::GROUP_BARRED;
|
||||
} else if (v == "deny") {
|
||||
m_groupData.deny = true;
|
||||
m_groupRules |= Desktop::View::GROUP_DENY;
|
||||
} else if (v == "override") {
|
||||
// Clear existing rules
|
||||
m_groupRules = Desktop::View::GROUP_OVERRIDE;
|
||||
|
|
@ -2213,9 +1915,9 @@ void CWindow::mapWindow() {
|
|||
m_isFloating = true;
|
||||
|
||||
if (PWORKSPACE->m_defaultPseudo) {
|
||||
m_isPseudotiled = true;
|
||||
CBox desiredGeometry = g_pXWaylandManager->getGeometryForWindow(m_self.lock());
|
||||
m_pseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
|
||||
m_target->setPseudoSize(Vector2D{desiredGeometry.width, desiredGeometry.height});
|
||||
m_target->setPseudo(true);
|
||||
}
|
||||
|
||||
updateWindowData();
|
||||
|
|
@ -2230,43 +1932,29 @@ void CWindow::mapWindow() {
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{"openwindow", std::format("{:x},{},{},{}", m_self.lock(), PWORKSPACE->m_name, m_class, m_title)});
|
||||
EMIT_HOOK_EVENT("openWindowEarly", m_self.lock());
|
||||
|
||||
if (*PAUTOGROUP // auto_group enabled
|
||||
&& Desktop::focusState()->window() // focused window exists
|
||||
&& canBeGroupedInto(Desktop::focusState()->window()->m_group) // we can group
|
||||
&& Desktop::focusState()->window()->m_workspace == m_workspace // workspaces match, we're not opening on another ws
|
||||
&& !isModal() && !(parent() && m_isFloating) && !isX11OverrideRedirect() // not a modal, floating child or X11 OR
|
||||
) {
|
||||
// add to group if we are focused on one
|
||||
Desktop::focusState()->window()->m_group->add(m_self.lock());
|
||||
} else
|
||||
g_layoutManager->newTarget(m_target, m_workspace->m_space);
|
||||
|
||||
if (!m_group && (m_groupRules & GROUP_SET))
|
||||
m_group = CGroup::create({m_self});
|
||||
|
||||
if (m_isFloating) {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(m_self.lock());
|
||||
m_createdOverFullscreen = true;
|
||||
|
||||
if (!m_ruleApplicator->static_.size.empty()) {
|
||||
const auto COMPUTED = calculateExpression(m_ruleApplicator->static_.size);
|
||||
if (!COMPUTED)
|
||||
Log::logger->log(Log::ERR, "failed to parse {} as an expression", m_ruleApplicator->static_.size);
|
||||
else {
|
||||
*m_realSize = *COMPUTED;
|
||||
setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_ruleApplicator->static_.position.empty()) {
|
||||
const auto COMPUTED = calculateExpression(m_ruleApplicator->static_.position);
|
||||
if (!COMPUTED)
|
||||
Log::logger->log(Log::ERR, "failed to parse {} as an expression", m_ruleApplicator->static_.position);
|
||||
else {
|
||||
*m_realPosition = *COMPUTED + PMONITOR->m_position;
|
||||
setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ruleApplicator->static_.center.value_or(false)) {
|
||||
const auto WORKAREA = PMONITOR->logicalBoxMinusReserved();
|
||||
*m_realPosition = WORKAREA.middle() - m_realSize->goal() / 2.f;
|
||||
}
|
||||
|
||||
// set the pseudo size to the GOAL of our current size
|
||||
// because the windows are animated on RealSize
|
||||
m_pseudoSize = m_realSize->goal();
|
||||
m_target->setPseudoSize(m_realSize->goal());
|
||||
|
||||
g_pCompositor->changeWindowZOrder(m_self.lock(), true);
|
||||
} else {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(m_self.lock());
|
||||
|
||||
bool setPseudo = false;
|
||||
|
||||
if (!m_ruleApplicator->static_.size.empty()) {
|
||||
|
|
@ -2274,14 +1962,14 @@ void CWindow::mapWindow() {
|
|||
if (!COMPUTED)
|
||||
Log::logger->log(Log::ERR, "failed to parse {} as an expression", m_ruleApplicator->static_.size);
|
||||
else {
|
||||
setPseudo = true;
|
||||
m_pseudoSize = *COMPUTED;
|
||||
setPseudo = true;
|
||||
m_target->setPseudoSize(*COMPUTED);
|
||||
setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!setPseudo)
|
||||
m_pseudoSize = m_realSize->goal() - Vector2D(10, 10);
|
||||
m_target->setPseudoSize(m_realSize->goal() - Vector2D(10, 10));
|
||||
}
|
||||
|
||||
const auto PFOCUSEDWINDOWPREV = Desktop::focusState()->window();
|
||||
|
|
@ -2311,13 +1999,13 @@ void CWindow::mapWindow() {
|
|||
(!PFORCEFOCUS || PFORCEFOCUS == m_self.lock()) && !g_pInputManager->isConstrained()) {
|
||||
|
||||
// this window should gain focus: if it's grouped, preserve fullscreen state.
|
||||
const bool SAME_GROUP = hasInGroup(LAST_FOCUS_WINDOW);
|
||||
const bool SAME_GROUP = m_group && m_group->has(LAST_FOCUS_WINDOW);
|
||||
|
||||
if (IS_LAST_IN_FS && SAME_GROUP) {
|
||||
Desktop::focusState()->rawWindowFocus(m_self.lock());
|
||||
Desktop::focusState()->rawWindowFocus(m_self.lock(), FOCUS_REASON_NEW_WINDOW);
|
||||
g_pCompositor->setWindowFullscreenInternal(m_self.lock(), LAST_FS_MODE);
|
||||
} else
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock());
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_NEW_WINDOW);
|
||||
|
||||
m_activeInactiveAlpha->setValueAndWarp(*PACTIVEALPHA);
|
||||
m_dimPercent->setValueAndWarp(m_ruleApplicator->noDim().valueOrDefault() ? 0.f : *PDIMSTRENGTH);
|
||||
|
|
@ -2358,18 +2046,16 @@ void CWindow::mapWindow() {
|
|||
|
||||
if (workspaceSilent) {
|
||||
if (validMapped(PFOCUSEDWINDOWPREV)) {
|
||||
Desktop::focusState()->rawWindowFocus(PFOCUSEDWINDOWPREV);
|
||||
Desktop::focusState()->rawWindowFocus(PFOCUSEDWINDOWPREV, FOCUS_REASON_NEW_WINDOW);
|
||||
PFOCUSEDWINDOWPREV->updateWindowDecos(); // need to for some reason i cba to find out why
|
||||
} else if (!PFOCUSEDWINDOWPREV)
|
||||
Desktop::focusState()->rawWindowFocus(nullptr);
|
||||
Desktop::focusState()->rawWindowFocus(nullptr, FOCUS_REASON_NEW_WINDOW);
|
||||
}
|
||||
|
||||
// swallow
|
||||
if (SWALLOWER) {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(SWALLOWER);
|
||||
g_pHyprRenderer->damageWindow(SWALLOWER);
|
||||
g_layoutManager->removeTarget(SWALLOWER->layoutTarget());
|
||||
SWALLOWER->setHidden(true);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitorID());
|
||||
}
|
||||
|
||||
m_firstMap = false;
|
||||
|
|
@ -2382,7 +2068,7 @@ void CWindow::mapWindow() {
|
|||
// apply data from default decos. Borders, shadows.
|
||||
g_pDecorationPositioner->forceRecalcFor(m_self.lock());
|
||||
updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(m_self.lock());
|
||||
layoutTarget()->recalc();
|
||||
|
||||
// do animations
|
||||
g_pDesktopAnimationManager->startAnimation(m_self.lock(), CDesktopAnimationManager::ANIMATION_TYPE_IN);
|
||||
|
|
@ -2454,10 +2140,10 @@ void CWindow::unmapWindow() {
|
|||
m_swallowed->m_currentlySwallowed = false;
|
||||
m_swallowed->setHidden(false);
|
||||
|
||||
if (m_groupData.pNextWindow.lock())
|
||||
if (m_group)
|
||||
m_swallowed->m_groupSwallowed = true; // flag for the swallowed window to be created into the group where it belongs when auto_group = false.
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(m_swallowed.lock());
|
||||
g_layoutManager->newTarget(m_swallowed->layoutTarget(), m_workspace->m_space);
|
||||
}
|
||||
|
||||
m_swallowed->m_groupSwallowed = false;
|
||||
|
|
@ -2466,7 +2152,7 @@ void CWindow::unmapWindow() {
|
|||
|
||||
bool wasLastWindow = false;
|
||||
PHLWINDOW nextInGroup = [this] -> PHLWINDOW {
|
||||
if (!m_groupData.pNextWindow)
|
||||
if (!m_group)
|
||||
return nullptr;
|
||||
|
||||
// walk the history to find a suitable window
|
||||
|
|
@ -2475,7 +2161,7 @@ void CWindow::unmapWindow() {
|
|||
if (!w || !w->m_isMapped || w == m_self)
|
||||
continue;
|
||||
|
||||
if (!hasInGroup(w.lock()))
|
||||
if (!m_group->has(w.lock()))
|
||||
continue;
|
||||
|
||||
return w.lock();
|
||||
|
|
@ -2491,7 +2177,7 @@ void CWindow::unmapWindow() {
|
|||
g_pInputManager->releaseAllMouseButtons();
|
||||
}
|
||||
|
||||
if (m_self.lock() == g_pInputManager->m_currentlyDraggedWindow.lock())
|
||||
if (m_self.lock() == g_layoutManager->dragController()->target())
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
|
||||
// remove the fullscreen window status from workspace if we closed it
|
||||
|
|
@ -2500,7 +2186,10 @@ void CWindow::unmapWindow() {
|
|||
if (PWORKSPACE->m_hasFullscreenWindow && isFullscreen())
|
||||
PWORKSPACE->m_hasFullscreenWindow = false;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(m_self.lock());
|
||||
if (m_group)
|
||||
m_group->remove(m_self.lock());
|
||||
|
||||
g_layoutManager->removeTarget(m_target);
|
||||
|
||||
g_pHyprRenderer->damageWindow(m_self.lock());
|
||||
|
||||
|
|
@ -2516,17 +2205,20 @@ void CWindow::unmapWindow() {
|
|||
if (*FOCUSONCLOSE)
|
||||
candidate = (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(),
|
||||
Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING));
|
||||
else
|
||||
candidate = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(m_self.lock());
|
||||
else {
|
||||
const auto CAND = g_layoutManager->getNextCandidate(m_workspace->m_space, layoutTarget());
|
||||
if (CAND)
|
||||
candidate = CAND->window();
|
||||
}
|
||||
}
|
||||
|
||||
Log::logger->log(Log::DEBUG, "On closed window, new focused candidate is {}", candidate);
|
||||
|
||||
if (candidate != Desktop::focusState()->window() && candidate) {
|
||||
if (candidate == nextInGroup)
|
||||
Desktop::focusState()->rawWindowFocus(candidate);
|
||||
Desktop::focusState()->rawWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
else
|
||||
Desktop::focusState()->fullWindowFocus(candidate);
|
||||
Desktop::focusState()->fullWindowFocus(candidate, FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
|
||||
if ((*PEXITRETAINSFS || candidate == nextInGroup) && CURRENTWINDOWFSSTATE)
|
||||
g_pCompositor->setWindowFullscreenInternal(candidate, CURRENTFSMODE);
|
||||
|
|
@ -2541,7 +2233,8 @@ void CWindow::unmapWindow() {
|
|||
if (m_self.lock() == Desktop::focusState()->window() || !Desktop::focusState()->window()) {
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""});
|
||||
EMIT_HOOK_EVENT("activeWindow", PHLWINDOW{nullptr});
|
||||
|
||||
EMIT_HOOK_EVENT("activeWindow", Desktop::View::SWindowActiveEvent{nullptr COMMA FOCUS_REASON_OTHER});
|
||||
}
|
||||
} else {
|
||||
Log::logger->log(Log::DEBUG, "Unmapped was not focused, ignoring a refocus.");
|
||||
|
|
@ -2573,7 +2266,13 @@ void CWindow::commitWindow() {
|
|||
// try to calculate static rules already for any floats
|
||||
m_ruleApplicator->readStaticRules(true);
|
||||
|
||||
Vector2D predSize = g_pLayoutManager->getCurrentLayout()->predictSizeForNewWindow(m_self.lock());
|
||||
const Vector2D predSize = !m_ruleApplicator->static_.floating.value_or(false) // no float rule
|
||||
&& !m_isFloating // not floating
|
||||
&& !parent() // no parents
|
||||
&& !g_pXWaylandManager->shouldBeFloated(m_self.lock(), true) // should not be floated
|
||||
?
|
||||
g_layoutManager->predictSizeForNewTiledTarget().value_or(Vector2D{}) :
|
||||
Vector2D{};
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Layout predicts size {} for {}", predSize, m_self.lock());
|
||||
|
||||
|
|
@ -2634,7 +2333,7 @@ void CWindow::destroyWindow() {
|
|||
|
||||
m_listeners = {};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(m_self.lock());
|
||||
g_layoutManager->removeTarget(m_target);
|
||||
|
||||
m_readyToDelete = true;
|
||||
|
||||
|
|
@ -2664,7 +2363,7 @@ void CWindow::activateX11() {
|
|||
if (!m_xwaylandSurface->wantsFocus())
|
||||
return;
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock());
|
||||
Desktop::focusState()->fullWindowFocus(m_self.lock(), FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2764,3 +2463,26 @@ std::optional<Vector2D> CWindow::maxSize() {
|
|||
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
SP<Layout::ITarget> CWindow::layoutTarget() {
|
||||
return m_group ? m_group->m_target : m_target;
|
||||
}
|
||||
|
||||
bool CWindow::canBeGroupedInto(SP<CGroup> group) {
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
if (isX11OverrideRedirect())
|
||||
return false;
|
||||
|
||||
static auto ALLOWGROUPMERGE = CConfigValue<Hyprlang::INT>("group:merge_groups_on_drag");
|
||||
bool isGroup = m_group;
|
||||
bool disallowDragIntoGroup = g_layoutManager->dragController()->wasDraggingWindow() && isGroup && !sc<bool>(*ALLOWGROUPMERGE);
|
||||
return !g_pKeybindManager->m_groupsLocked // global group lock disengaged
|
||||
&& ((m_groupRules & GROUP_INVADE && m_firstMap) // window ignore local group locks, or
|
||||
|| (!group->locked() // target unlocked
|
||||
&& !(m_group && m_group->locked()))) // source unlocked or isn't group
|
||||
&& !(m_groupRules & GROUP_DENY) // source is not denied entry
|
||||
&& !(m_groupRules & GROUP_BARRED && m_firstMap) // group rule doesn't prevent adding window
|
||||
&& !disallowDragIntoGroup; // config allows groups to be merged
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,19 @@ struct SWorkspaceRule;
|
|||
|
||||
class IWindowTransformer;
|
||||
|
||||
namespace Layout {
|
||||
class ITarget;
|
||||
class CWindowTarget;
|
||||
}
|
||||
|
||||
namespace Desktop {
|
||||
enum eFocusReason : uint8_t;
|
||||
}
|
||||
|
||||
namespace Desktop::View {
|
||||
|
||||
class CGroup;
|
||||
|
||||
enum eGroupRules : uint8_t {
|
||||
// effective only during first map, except for _ALWAYS variant
|
||||
GROUP_NONE = 0,
|
||||
|
|
@ -38,6 +49,7 @@ namespace Desktop::View {
|
|||
GROUP_LOCK_ALWAYS = 1 << 4,
|
||||
GROUP_INVADE = 1 << 5, // Force enter a group, event if lock is engaged
|
||||
GROUP_OVERRIDE = 1 << 6, // Override other rules
|
||||
GROUP_DENY = 1 << 7, // deny
|
||||
};
|
||||
|
||||
enum eGetWindowProperties : uint8_t {
|
||||
|
|
@ -61,6 +73,11 @@ namespace Desktop::View {
|
|||
SUPPRESS_FULLSCREEN_OUTPUT = 1 << 4,
|
||||
};
|
||||
|
||||
struct SWindowActiveEvent {
|
||||
PHLWINDOW window = nullptr;
|
||||
eFocusReason reason = sc<eFocusReason>(0) /* unknown */;
|
||||
};
|
||||
|
||||
struct SInitialWorkspaceToken {
|
||||
PHLWINDOWREF primaryOwner;
|
||||
std::string workspace;
|
||||
|
|
@ -97,6 +114,8 @@ namespace Desktop::View {
|
|||
WP<CXDGSurfaceResource> m_xdgSurface;
|
||||
WP<CXWaylandSurface> m_xwaylandSurface;
|
||||
|
||||
SP<Layout::ITarget> m_target;
|
||||
|
||||
// this is the position and size of the "bounding box"
|
||||
Vector2D m_position = Vector2D(0, 0);
|
||||
Vector2D m_size = Vector2D(0, 0);
|
||||
|
|
@ -112,23 +131,14 @@ namespace Desktop::View {
|
|||
std::optional<std::pair<uint32_t, Vector2D>> m_pendingSizeAck;
|
||||
std::vector<std::pair<uint32_t, Vector2D>> m_pendingSizeAcks;
|
||||
|
||||
// for restoring floating statuses
|
||||
Vector2D m_lastFloatingSize;
|
||||
Vector2D m_lastFloatingPosition;
|
||||
|
||||
// for floating window offset in workspace animations
|
||||
Vector2D m_floatingOffset = Vector2D(0, 0);
|
||||
|
||||
// this is used for pseudotiling
|
||||
bool m_isPseudotiled = false;
|
||||
Vector2D m_pseudoSize = Vector2D(1280, 720);
|
||||
|
||||
// for recovering relative cursor position
|
||||
Vector2D m_relativeCursorCoordsOnLastWarp = Vector2D(-1, -1);
|
||||
|
||||
bool m_firstMap = false; // for layouts
|
||||
bool m_isFloating = false;
|
||||
bool m_draggingTiled = false; // for dragging around tiled windows
|
||||
SFullscreenState m_fullscreenState = {.internal = FSMODE_NONE, .client = FSMODE_NONE};
|
||||
std::string m_title = "";
|
||||
std::string m_class = "";
|
||||
|
|
@ -229,15 +239,10 @@ namespace Desktop::View {
|
|||
std::string m_initialWorkspaceToken = "";
|
||||
|
||||
// for groups
|
||||
struct SGroupData {
|
||||
PHLWINDOWREF pNextWindow; // nullptr means no grouping. Self means single group.
|
||||
bool head = false;
|
||||
bool locked = false; // per group lock
|
||||
bool deny = false; // deny window from enter a group or made a group
|
||||
} m_groupData;
|
||||
uint16_t m_groupRules = Desktop::View::GROUP_NONE;
|
||||
SP<CGroup> m_group;
|
||||
uint16_t m_groupRules = Desktop::View::GROUP_NONE;
|
||||
|
||||
bool m_tearingHint = false;
|
||||
bool m_tearingHint = false;
|
||||
|
||||
// Stable ID for ext_foreign_toplevel_list
|
||||
const uint64_t m_stableID = 0x2137;
|
||||
|
|
@ -303,21 +308,6 @@ namespace Desktop::View {
|
|||
bool isInCurvedCorner(double x, double y);
|
||||
bool hasPopupAt(const Vector2D& pos);
|
||||
int popupsCount();
|
||||
void applyGroupRules();
|
||||
void createGroup();
|
||||
void destroyGroup();
|
||||
PHLWINDOW getGroupHead();
|
||||
PHLWINDOW getGroupTail();
|
||||
PHLWINDOW getGroupCurrent();
|
||||
PHLWINDOW getGroupPrevious();
|
||||
PHLWINDOW getGroupWindowByIndex(int);
|
||||
bool hasInGroup(PHLWINDOW);
|
||||
int getGroupSize();
|
||||
bool canBeGroupedInto(PHLWINDOW pWindow);
|
||||
void setGroupCurrent(PHLWINDOW pWindow);
|
||||
void insertWindowToGroup(PHLWINDOW pWindow);
|
||||
void updateGroupOutputs();
|
||||
void switchWithWindowInGroup(PHLWINDOW pWindow);
|
||||
void setAnimationsToMove();
|
||||
void onWorkspaceAnimUpdate();
|
||||
void onFocusAnimUpdate();
|
||||
|
|
@ -350,6 +340,8 @@ namespace Desktop::View {
|
|||
std::optional<Vector2D> calculateExpression(const std::string& s);
|
||||
std::optional<Vector2D> minSize();
|
||||
std::optional<Vector2D> maxSize();
|
||||
SP<Layout::ITarget> layoutTarget();
|
||||
bool canBeGroupedInto(SP<CGroup> group);
|
||||
|
||||
CBox getWindowMainSurfaceBox() const {
|
||||
return {m_realPosition->value().x, m_realPosition->value().y, m_realSize->value().x, m_realSize->value().y};
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@
|
|||
#include "../protocols/core/DataDevice.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../hyprerror/HyprError.hpp"
|
||||
#include "../layout/LayoutManager.hpp"
|
||||
#include "../i18n/Engine.hpp"
|
||||
#include "sync/SyncTimeline.hpp"
|
||||
#include "time/Time.hpp"
|
||||
|
|
@ -306,7 +306,7 @@ void CMonitor::onConnect(bool noRule) {
|
|||
Desktop::focusState()->rawMonitorFocus(m_self.lock());
|
||||
|
||||
g_pHyprRenderer->arrangeLayersForMonitor(m_id);
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id);
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
|
||||
// ensure VRR (will enable if necessary)
|
||||
g_pConfigManager->ensureVRR(m_self.lock());
|
||||
|
|
@ -1119,7 +1119,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
|
|||
// workspace exists, move it to the newly connected monitor
|
||||
g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, m_self.lock());
|
||||
m_activeWorkspace = PNEWWORKSPACE;
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id);
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
g_pDesktopAnimationManager->startAnimation(PNEWWORKSPACE, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
|
||||
} else {
|
||||
if (newDefaultWorkspaceName.empty())
|
||||
|
|
@ -1323,13 +1323,13 @@ void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bo
|
|||
pWindow = pWorkspace->getFirstWindow();
|
||||
}
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(pWindow);
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||
}
|
||||
|
||||
if (!noMouseMove)
|
||||
g_pInputManager->simulateMouseMovement();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id);
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_name});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_id, pWorkspace->m_name)});
|
||||
|
|
@ -1392,11 +1392,11 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
if (POLDSPECIAL)
|
||||
POLDSPECIAL->m_events.activeChanged.emit();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id);
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
|
||||
if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
|
||||
if (const auto PLAST = m_activeWorkspace->getLastFocusedWindow(); PLAST)
|
||||
Desktop::focusState()->fullWindowFocus(PLAST);
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND);
|
||||
else
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
|
@ -1421,7 +1421,7 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
const auto PMONITORWORKSPACEOWNER = pWorkspace->m_monitor.lock();
|
||||
if (const auto PMWSOWNER = pWorkspace->m_monitor.lock(); PMWSOWNER && PMWSOWNER->m_activeSpecialWorkspace == pWorkspace) {
|
||||
PMWSOWNER->m_activeSpecialWorkspace.reset();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMWSOWNER->m_id);
|
||||
g_layoutManager->recalculateMonitor(PMWSOWNER);
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMWSOWNER->m_name});
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"activespecialv2", ",," + PMWSOWNER->m_name});
|
||||
|
||||
|
|
@ -1480,17 +1480,16 @@ void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
|
|||
} else
|
||||
pos = pos - PMONFROMMIDDLE->m_position + m_position;
|
||||
|
||||
*w->m_realPosition = pos;
|
||||
w->m_position = pos;
|
||||
w->layoutTarget()->setPositionGlobal(CBox{pos, w->layoutTarget()->position().size()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m_id);
|
||||
g_layoutManager->recalculateMonitor(m_self.lock());
|
||||
|
||||
if (!(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
|
||||
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
|
||||
Desktop::focusState()->fullWindowFocus(PLAST);
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND);
|
||||
else
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
|
|
|||
0
src/helpers/math/Direction.cpp
Normal file
0
src/helpers/math/Direction.cpp
Normal file
35
src/helpers/math/Direction.hpp
Normal file
35
src/helpers/math/Direction.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Math {
|
||||
enum eDirection : int8_t {
|
||||
DIRECTION_DEFAULT = -1,
|
||||
DIRECTION_UP,
|
||||
DIRECTION_RIGHT,
|
||||
DIRECTION_DOWN,
|
||||
DIRECTION_LEFT
|
||||
};
|
||||
|
||||
inline eDirection fromChar(char x) {
|
||||
switch (x) {
|
||||
case 'r': return DIRECTION_RIGHT;
|
||||
case 'l': return DIRECTION_LEFT;
|
||||
case 't':
|
||||
case 'u': return DIRECTION_UP;
|
||||
case 'b':
|
||||
case 'd': return DIRECTION_DOWN;
|
||||
default: return DIRECTION_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
inline const char* toString(eDirection d) {
|
||||
switch (d) {
|
||||
case DIRECTION_UP: return "up";
|
||||
case DIRECTION_DOWN: return "down";
|
||||
case DIRECTION_LEFT: return "left";
|
||||
case DIRECTION_RIGHT: return "right";
|
||||
default: return "default";
|
||||
}
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,110 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "IHyprLayout.hpp"
|
||||
#include "../desktop/DesktopTypes.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <format>
|
||||
|
||||
class CHyprDwindleLayout;
|
||||
enum eFullscreenMode : int8_t;
|
||||
|
||||
struct SDwindleNodeData {
|
||||
WP<SDwindleNodeData> pParent;
|
||||
bool isNode = false;
|
||||
|
||||
PHLWINDOWREF pWindow;
|
||||
|
||||
std::array<WP<SDwindleNodeData>, 2> children = {};
|
||||
WP<SDwindleNodeData> self;
|
||||
|
||||
bool splitTop = false; // for preserve_split
|
||||
|
||||
CBox box = {0};
|
||||
|
||||
WORKSPACEID workspaceID = WORKSPACE_INVALID;
|
||||
|
||||
float splitRatio = 1.f;
|
||||
|
||||
bool valid = true;
|
||||
|
||||
bool ignoreFullscreenChecks = false;
|
||||
|
||||
// For list lookup
|
||||
bool operator==(const SDwindleNodeData& rhs) const {
|
||||
return pWindow.lock() == rhs.pWindow.lock() && workspaceID == rhs.workspaceID && box == rhs.box && pParent == rhs.pParent && children[0] == rhs.children[0] &&
|
||||
children[1] == rhs.children[1];
|
||||
}
|
||||
|
||||
void recalcSizePosRecursive(bool force = false, bool horizontalOverride = false, bool verticalOverride = false);
|
||||
void applyRootBox();
|
||||
CHyprDwindleLayout* layout = nullptr;
|
||||
};
|
||||
|
||||
class CHyprDwindleLayout : public IHyprLayout {
|
||||
public:
|
||||
virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT);
|
||||
virtual void onWindowRemovedTiling(PHLWINDOW);
|
||||
virtual bool isWindowTiled(PHLWINDOW);
|
||||
virtual void recalculateMonitor(const MONITORID&);
|
||||
virtual void recalculateWindow(PHLWINDOW);
|
||||
virtual void onBeginDragWindow();
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE);
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW);
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW);
|
||||
virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent);
|
||||
virtual void alterSplitRatio(PHLWINDOW, float, bool);
|
||||
virtual std::string getLayoutName();
|
||||
virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to);
|
||||
virtual Vector2D predictSizeForNewWindowTiled();
|
||||
|
||||
virtual void onEnable();
|
||||
virtual void onDisable();
|
||||
|
||||
private:
|
||||
std::vector<SP<SDwindleNodeData>> m_dwindleNodesData;
|
||||
|
||||
struct {
|
||||
bool started = false;
|
||||
bool pseudo = false;
|
||||
bool xExtent = false;
|
||||
bool yExtent = false;
|
||||
} m_pseudoDragFlags;
|
||||
|
||||
std::optional<Vector2D> m_overrideFocalPoint; // for onWindowCreatedTiling.
|
||||
|
||||
int getNodesOnWorkspace(const WORKSPACEID&);
|
||||
void applyNodeDataToWindow(SP<SDwindleNodeData>, bool force = false);
|
||||
void calculateWorkspace(const PHLWORKSPACE& pWorkspace);
|
||||
SP<SDwindleNodeData> getNodeFromWindow(PHLWINDOW);
|
||||
SP<SDwindleNodeData> getFirstNodeOnWorkspace(const WORKSPACEID&);
|
||||
SP<SDwindleNodeData> getClosestNodeOnWorkspace(const WORKSPACEID&, const Vector2D&);
|
||||
SP<SDwindleNodeData> getMasterNodeOnWorkspace(const WORKSPACEID&);
|
||||
|
||||
void toggleSplit(PHLWINDOW);
|
||||
void swapSplit(PHLWINDOW);
|
||||
void moveToRoot(PHLWINDOW, bool stable = true);
|
||||
|
||||
eDirection m_overrideDirection = DIRECTION_DEFAULT;
|
||||
|
||||
friend struct SDwindleNodeData;
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
struct std::formatter<SP<SDwindleNodeData>, CharT> : std::formatter<CharT> {
|
||||
template <typename FormatContext>
|
||||
auto format(const SP<SDwindleNodeData>& node, FormatContext& ctx) const {
|
||||
auto out = ctx.out();
|
||||
if (!node)
|
||||
return std::format_to(out, "[Node nullptr]");
|
||||
std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", rc<uintptr_t>(node.get()), node->workspaceID, node->box.pos(), node->box.size());
|
||||
if (!node->isNode && !node->pWindow.expired())
|
||||
std::format_to(out, ", window: {:x}", node->pWindow.lock());
|
||||
return std::format_to(out, "]");
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,248 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include <any>
|
||||
|
||||
class CGradientValueData;
|
||||
|
||||
struct SWindowRenderLayoutHints {
|
||||
bool isBorderGradient = false;
|
||||
CGradientValueData* borderGradient = nullptr;
|
||||
};
|
||||
|
||||
struct SLayoutMessageHeader {
|
||||
PHLWINDOW pWindow;
|
||||
};
|
||||
|
||||
enum eFullscreenMode : int8_t;
|
||||
|
||||
enum eRectCorner : uint8_t {
|
||||
CORNER_NONE = 0,
|
||||
CORNER_TOPLEFT = (1 << 0),
|
||||
CORNER_TOPRIGHT = (1 << 1),
|
||||
CORNER_BOTTOMRIGHT = (1 << 2),
|
||||
CORNER_BOTTOMLEFT = (1 << 3),
|
||||
};
|
||||
|
||||
inline eRectCorner cornerFromBox(const CBox& box, const Vector2D& pos) {
|
||||
const auto CENTER = box.middle();
|
||||
|
||||
if (pos.x < CENTER.x)
|
||||
return pos.y < CENTER.y ? CORNER_TOPLEFT : CORNER_BOTTOMLEFT;
|
||||
return pos.y < CENTER.y ? CORNER_TOPRIGHT : CORNER_BOTTOMRIGHT;
|
||||
}
|
||||
|
||||
enum eSnapEdge : uint8_t {
|
||||
SNAP_INVALID = 0,
|
||||
SNAP_UP = (1 << 0),
|
||||
SNAP_DOWN = (1 << 1),
|
||||
SNAP_LEFT = (1 << 2),
|
||||
SNAP_RIGHT = (1 << 3),
|
||||
};
|
||||
|
||||
enum eDirection : int8_t {
|
||||
DIRECTION_DEFAULT = -1,
|
||||
DIRECTION_UP = 0,
|
||||
DIRECTION_RIGHT,
|
||||
DIRECTION_DOWN,
|
||||
DIRECTION_LEFT
|
||||
};
|
||||
|
||||
class IHyprLayout {
|
||||
public:
|
||||
virtual ~IHyprLayout() = default;
|
||||
virtual void onEnable() = 0;
|
||||
virtual void onDisable() = 0;
|
||||
|
||||
/*
|
||||
Called when a window is created (mapped)
|
||||
The layout HAS TO set the goal pos and size (anim mgr will use it)
|
||||
If !animationinprogress, then the anim mgr will not apply an anim.
|
||||
*/
|
||||
virtual void onWindowCreated(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT);
|
||||
virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT) = 0;
|
||||
virtual void onWindowCreatedFloating(PHLWINDOW);
|
||||
virtual bool onWindowCreatedAutoGroup(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Return tiled status
|
||||
*/
|
||||
virtual bool isWindowTiled(PHLWINDOW) = 0;
|
||||
|
||||
/*
|
||||
Called when a window is removed (unmapped)
|
||||
*/
|
||||
virtual void onWindowRemoved(PHLWINDOW);
|
||||
virtual void onWindowRemovedTiling(PHLWINDOW) = 0;
|
||||
virtual void onWindowRemovedFloating(PHLWINDOW);
|
||||
/*
|
||||
Called when the monitor requires a layout recalculation
|
||||
this usually means reserved area changes
|
||||
*/
|
||||
virtual void recalculateMonitor(const MONITORID&) = 0;
|
||||
|
||||
/*
|
||||
Called when the compositor requests a window
|
||||
to be recalculated, e.g. when pseudo is toggled.
|
||||
*/
|
||||
virtual void recalculateWindow(PHLWINDOW) = 0;
|
||||
|
||||
/*
|
||||
Called when a window is requested to be floated
|
||||
*/
|
||||
virtual void changeWindowFloatingMode(PHLWINDOW);
|
||||
/*
|
||||
Called when a window is clicked on, beginning a drag
|
||||
this might be a resize, move, whatever the layout defines it
|
||||
as.
|
||||
*/
|
||||
virtual void onBeginDragWindow();
|
||||
/*
|
||||
Called when a user requests a resize of the current window by a vec
|
||||
Vector2D holds pixel values
|
||||
Optional pWindow for a specific window
|
||||
*/
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr) = 0;
|
||||
/*
|
||||
Called when a user requests a move of the current window by a vec
|
||||
Vector2D holds pixel values
|
||||
Optional pWindow for a specific window
|
||||
*/
|
||||
virtual void moveActiveWindow(const Vector2D&, PHLWINDOW pWindow = nullptr);
|
||||
/*
|
||||
Called when a window is ended being dragged
|
||||
(mouse up)
|
||||
*/
|
||||
virtual void onEndDragWindow();
|
||||
/*
|
||||
Called whenever the mouse moves, should the layout want to
|
||||
do anything with it.
|
||||
Useful for dragging.
|
||||
*/
|
||||
virtual void onMouseMove(const Vector2D&);
|
||||
|
||||
/*
|
||||
Called when a window / the user requests to toggle the fullscreen state of a window
|
||||
The layout sets all the fullscreen flags.
|
||||
It can either accept or ignore.
|
||||
*/
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE) = 0;
|
||||
|
||||
/*
|
||||
Called when a dispatcher requests a custom message
|
||||
The layout is free to ignore.
|
||||
std::any is the reply. Can be empty.
|
||||
*/
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string) = 0;
|
||||
|
||||
/*
|
||||
Required to be handled, but may return just SWindowRenderLayoutHints()
|
||||
Called when the renderer requests any special draw flags for
|
||||
a specific window, e.g. border color for groups.
|
||||
*/
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW) = 0;
|
||||
|
||||
/*
|
||||
Called when the user requests two windows to be swapped places.
|
||||
The layout is free to ignore.
|
||||
*/
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW) = 0;
|
||||
|
||||
/*
|
||||
Called when the user requests a window move in a direction.
|
||||
The layout is free to ignore.
|
||||
*/
|
||||
virtual void moveWindowTo(PHLWINDOW, const std::string& direction, bool silent = false) = 0;
|
||||
|
||||
/*
|
||||
Called when the user requests to change the splitratio by or to X
|
||||
on a window
|
||||
*/
|
||||
virtual void alterSplitRatio(PHLWINDOW, float, bool exact = false) = 0;
|
||||
|
||||
/*
|
||||
Called when something wants the current layout's name
|
||||
*/
|
||||
virtual std::string getLayoutName() = 0;
|
||||
|
||||
/*
|
||||
Called for getting the next candidate for a focus
|
||||
*/
|
||||
virtual PHLWINDOW getNextWindowCandidate(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Internal: called when window focus changes
|
||||
*/
|
||||
virtual void onWindowFocusChange(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Called for replacing any data a layout has for a new window
|
||||
*/
|
||||
virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to) = 0;
|
||||
|
||||
/*
|
||||
Determines if a window can be focused. If hidden this usually means the window is part of a group.
|
||||
*/
|
||||
virtual bool isWindowReachable(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Called before an attempt is made to focus a window.
|
||||
Brings the window to the top of any groups and ensures it is not hidden.
|
||||
If the window is unmapped following this call, the focus attempt will fail.
|
||||
*/
|
||||
virtual void bringWindowToTop(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Called via the foreign toplevel activation protocol.
|
||||
Focuses a window, bringing it to the top of its group if applicable.
|
||||
May be ignored.
|
||||
*/
|
||||
virtual void requestFocusForWindow(PHLWINDOW);
|
||||
|
||||
/*
|
||||
Called to predict the size of a newly opened window to send it a configure.
|
||||
Return 0,0 if unpredictable
|
||||
*/
|
||||
virtual Vector2D predictSizeForNewWindowTiled() = 0;
|
||||
|
||||
/*
|
||||
Prefer not overriding, use predictSizeForNewWindowTiled.
|
||||
*/
|
||||
virtual Vector2D predictSizeForNewWindow(PHLWINDOW pWindow);
|
||||
virtual Vector2D predictSizeForNewWindowFloating(PHLWINDOW pWindow);
|
||||
|
||||
/*
|
||||
Called to try to pick up window for dragging.
|
||||
Updates drag related variables and floats window if threshold reached.
|
||||
Return true to reject
|
||||
*/
|
||||
virtual bool updateDragWindow();
|
||||
|
||||
/*
|
||||
Triggers a window snap event
|
||||
*/
|
||||
virtual void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, PHLWINDOW DRAGGINGWINDOW, const eMouseBindMode MODE, const int CORNER, const Vector2D& BEGINSIZE);
|
||||
|
||||
/*
|
||||
Fits a floating window on its monitor
|
||||
*/
|
||||
virtual void fitFloatingWindowOnMonitor(PHLWINDOW w, std::optional<CBox> targetBox = std::nullopt);
|
||||
|
||||
/*
|
||||
Returns a logical box describing the work area on a workspace
|
||||
(monitor size - reserved - gapsOut)
|
||||
*/
|
||||
virtual CBox workAreaOnWorkspace(const PHLWORKSPACE& pWorkspace);
|
||||
|
||||
private:
|
||||
int m_mouseMoveEventCount;
|
||||
Vector2D m_beginDragXY;
|
||||
Vector2D m_lastDragXY;
|
||||
Vector2D m_beginDragPositionXY;
|
||||
Vector2D m_beginDragSizeXY;
|
||||
Vector2D m_draggingWindowOriginalFloatSize;
|
||||
eRectCorner m_grabbedCorner = CORNER_TOPLEFT;
|
||||
|
||||
PHLWINDOWREF m_lastTiledWindow;
|
||||
};
|
||||
344
src/layout/LayoutManager.cpp
Normal file
344
src/layout/LayoutManager.cpp
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
#include "LayoutManager.hpp"
|
||||
|
||||
#include "space/Space.hpp"
|
||||
#include "target/Target.hpp"
|
||||
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../desktop/state/FocusState.hpp"
|
||||
#include "../desktop/view/Group.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
CLayoutManager::CLayoutManager() {
|
||||
static auto P = g_pHookSystem->hookDynamic("monitorLayoutChanged", [](void* hk, SCallbackInfo& info, std::any param) {
|
||||
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||
ws->m_space->recheckWorkArea();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CLayoutManager::newTarget(SP<ITarget> target, SP<CSpace> space) {
|
||||
// on a new target: remember desired pos for float, if available
|
||||
if (const auto DESIRED_GEOM = target->desiredGeometry(); DESIRED_GEOM)
|
||||
target->rememberFloatingSize(DESIRED_GEOM->size);
|
||||
|
||||
target->assignToSpace(space);
|
||||
}
|
||||
|
||||
void CLayoutManager::removeTarget(SP<ITarget> target) {
|
||||
target->assignToSpace(nullptr);
|
||||
}
|
||||
|
||||
void CLayoutManager::changeFloatingMode(SP<ITarget> target) {
|
||||
if (!target->space())
|
||||
return;
|
||||
|
||||
target->space()->toggleTargetFloating(target);
|
||||
}
|
||||
|
||||
void CLayoutManager::beginDragTarget(SP<ITarget> target, eMouseBindMode mode) {
|
||||
m_dragStateController->dragBegin(target, mode);
|
||||
}
|
||||
|
||||
void CLayoutManager::moveMouse(const Vector2D& mousePos) {
|
||||
m_dragStateController->mouseMove(mousePos);
|
||||
}
|
||||
|
||||
void CLayoutManager::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
if (target->isPseudo()) {
|
||||
auto fixedΔ = Δ;
|
||||
if (corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT)
|
||||
fixedΔ.x = -fixedΔ.x;
|
||||
if (corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT)
|
||||
fixedΔ.y = -fixedΔ.y;
|
||||
|
||||
auto newPseudoSize = target->pseudoSize() + fixedΔ;
|
||||
const auto TARGET_TILE_SIZE = target->position().size();
|
||||
newPseudoSize.x = std::clamp(newPseudoSize.x, MIN_WINDOW_SIZE, TARGET_TILE_SIZE.x);
|
||||
newPseudoSize.y = std::clamp(newPseudoSize.y, MIN_WINDOW_SIZE, TARGET_TILE_SIZE.y);
|
||||
|
||||
target->setPseudoSize(newPseudoSize);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
target->space()->resizeTarget(Δ, target, corner);
|
||||
}
|
||||
|
||||
std::expected<void, std::string> CLayoutManager::layoutMsg(const std::string_view& sv) {
|
||||
|
||||
const auto MONITOR = Desktop::focusState()->monitor();
|
||||
// forward to the active workspace
|
||||
if (!MONITOR)
|
||||
return std::unexpected("No monitor, can't find ws to target");
|
||||
|
||||
auto ws = MONITOR->m_activeSpecialWorkspace ? MONITOR->m_activeSpecialWorkspace : MONITOR->m_activeWorkspace;
|
||||
|
||||
if (!ws)
|
||||
return std::unexpected("No workspace, can't target");
|
||||
|
||||
return ws->m_space->layoutMsg(sv);
|
||||
}
|
||||
|
||||
void CLayoutManager::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
||||
if (!target->floating())
|
||||
return;
|
||||
|
||||
target->space()->moveTarget(Δ, target);
|
||||
}
|
||||
|
||||
void CLayoutManager::endDragTarget() {
|
||||
m_dragStateController->dragEnd();
|
||||
}
|
||||
|
||||
void CLayoutManager::fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode) {
|
||||
target->space()->setFullscreen(target, effectiveMode);
|
||||
}
|
||||
|
||||
void CLayoutManager::switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus) {
|
||||
|
||||
if (preserveFocus) {
|
||||
a->swap(b);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto IS_A_ACTIVE = Desktop::focusState()->window() == a->window();
|
||||
const auto IS_B_ACTIVE = Desktop::focusState()->window() == b->window();
|
||||
|
||||
a->swap(b);
|
||||
|
||||
if (IS_A_ACTIVE && b->window())
|
||||
Desktop::focusState()->fullWindowFocus(b->window(), Desktop::FOCUS_REASON_KEYBIND);
|
||||
|
||||
if (IS_B_ACTIVE && a->window())
|
||||
Desktop::focusState()->fullWindowFocus(a->window(), Desktop::FOCUS_REASON_KEYBIND);
|
||||
}
|
||||
|
||||
void CLayoutManager::moveInDirection(SP<ITarget> target, const std::string& direction, bool silent) {
|
||||
Math::eDirection dir = Math::fromChar(direction.at(0));
|
||||
if (dir == Math::DIRECTION_DEFAULT) {
|
||||
Log::logger->log(Log::ERR, "invalid direction for moveInDirection: {}", direction);
|
||||
return;
|
||||
}
|
||||
|
||||
target->space()->moveTargetInDirection(target, dir, silent);
|
||||
}
|
||||
|
||||
SP<ITarget> CLayoutManager::getNextCandidate(SP<CSpace> space, SP<ITarget> from) {
|
||||
return space->getNextCandidate(from);
|
||||
}
|
||||
|
||||
bool CLayoutManager::isReachable(SP<ITarget> target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLayoutManager::bringTargetToTop(SP<ITarget> target) {
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (target->window()->m_group) {
|
||||
// grouped, change the current to this window
|
||||
target->window()->m_group->setCurrent(target->window());
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CLayoutManager::predictSizeForNewTiledTarget() {
|
||||
const auto FOCUSED_MON = Desktop::focusState()->monitor();
|
||||
|
||||
if (!FOCUSED_MON || !FOCUSED_MON->m_activeWorkspace)
|
||||
return std::nullopt;
|
||||
|
||||
if (FOCUSED_MON->m_activeSpecialWorkspace)
|
||||
return FOCUSED_MON->m_activeSpecialWorkspace->m_space->predictSizeForNewTiledTarget();
|
||||
|
||||
return FOCUSED_MON->m_activeWorkspace->m_space->predictSizeForNewTiledTarget();
|
||||
}
|
||||
|
||||
const UP<Supplementary::CDragStateController>& CLayoutManager::dragController() {
|
||||
return m_dragStateController;
|
||||
}
|
||||
|
||||
static inline bool canSnap(const double SIDEA, const double SIDEB, const double GAP) {
|
||||
return std::abs(SIDEA - SIDEB) < GAP;
|
||||
}
|
||||
|
||||
static void snapMove(double& start, double& end, const double P) {
|
||||
end = P + (end - start);
|
||||
start = P;
|
||||
}
|
||||
|
||||
static void snapResize(double& start, double& end, const double P) {
|
||||
start = P;
|
||||
}
|
||||
|
||||
using SnapFn = std::function<void(double&, double&, const double)>;
|
||||
|
||||
void CLayoutManager::performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> DRAGGINGTARGET, const eMouseBindMode MODE, const int CORNER, const Vector2D& BEGINSIZE) {
|
||||
|
||||
const auto DRAGGINGWINDOW = DRAGGINGTARGET->window();
|
||||
|
||||
if (!Desktop::View::validMapped(DRAGGINGWINDOW))
|
||||
return;
|
||||
|
||||
static auto SNAPWINDOWGAP = CConfigValue<Hyprlang::INT>("general:snap:window_gap");
|
||||
static auto SNAPMONITORGAP = CConfigValue<Hyprlang::INT>("general:snap:monitor_gap");
|
||||
static auto SNAPBORDEROVERLAP = CConfigValue<Hyprlang::INT>("general:snap:border_overlap");
|
||||
static auto SNAPRESPECTGAPS = CConfigValue<Hyprlang::INT>("general:snap:respect_gaps");
|
||||
|
||||
static auto PGAPSIN = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
|
||||
static auto PGAPSOUT = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
|
||||
const auto GAPSNONE = CCssGapData{0, 0, 0, 0};
|
||||
|
||||
const SnapFn SNAP = (MODE == MBIND_MOVE) ? snapMove : snapResize;
|
||||
int snaps = 0;
|
||||
|
||||
struct SRange {
|
||||
double start = 0;
|
||||
double end = 0;
|
||||
};
|
||||
const auto EXTENTS = DRAGGINGWINDOW->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS);
|
||||
SRange sourceX = {sourcePos.x - EXTENTS.topLeft.x, sourcePos.x + sourceSize.x + EXTENTS.bottomRight.x};
|
||||
SRange sourceY = {sourcePos.y - EXTENTS.topLeft.y, sourcePos.y + sourceSize.y + EXTENTS.bottomRight.y};
|
||||
|
||||
if (*SNAPWINDOWGAP) {
|
||||
const double GAPSIZE = *SNAPWINDOWGAP;
|
||||
const auto WSID = DRAGGINGWINDOW->workspaceID();
|
||||
const bool HASFULLSCREEN = DRAGGINGWINDOW->m_workspace && DRAGGINGWINDOW->m_workspace->m_hasFullscreenWindow;
|
||||
|
||||
const auto* GAPSIN = *SNAPRESPECTGAPS ? sc<CCssGapData*>(PGAPSIN.ptr()->getData()) : &GAPSNONE;
|
||||
const double GAPSX = GAPSIN->m_left + GAPSIN->m_right;
|
||||
const double GAPSY = GAPSIN->m_top + GAPSIN->m_bottom;
|
||||
|
||||
for (auto& other : g_pCompositor->m_windows) {
|
||||
if ((HASFULLSCREEN && !other->m_createdOverFullscreen) || other == DRAGGINGWINDOW || other->workspaceID() != WSID || !other->m_isMapped || other->m_fadingOut ||
|
||||
other->isX11OverrideRedirect())
|
||||
continue;
|
||||
|
||||
const CBox SURF = other->getWindowBoxUnified(Desktop::View::RESERVED_EXTENTS);
|
||||
const SRange SURFBX = {SURF.x - GAPSX, SURF.x + SURF.w + GAPSX};
|
||||
const SRange SURFBY = {SURF.y - GAPSY, SURF.y + SURF.h + GAPSY};
|
||||
|
||||
// only snap windows if their ranges overlap in the opposite axis
|
||||
if (sourceY.start <= SURFBY.end && SURFBY.start <= sourceY.end) {
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && canSnap(sourceX.start, SURFBX.end, GAPSIZE)) {
|
||||
SNAP(sourceX.start, sourceX.end, SURFBX.end);
|
||||
snaps |= SNAP_LEFT;
|
||||
} else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && canSnap(sourceX.end, SURFBX.start, GAPSIZE)) {
|
||||
SNAP(sourceX.end, sourceX.start, SURFBX.start);
|
||||
snaps |= SNAP_RIGHT;
|
||||
}
|
||||
}
|
||||
if (sourceX.start <= SURFBX.end && SURFBX.start <= sourceX.end) {
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && canSnap(sourceY.start, SURFBY.end, GAPSIZE)) {
|
||||
SNAP(sourceY.start, sourceY.end, SURFBY.end);
|
||||
snaps |= SNAP_UP;
|
||||
} else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && canSnap(sourceY.end, SURFBY.start, GAPSIZE)) {
|
||||
SNAP(sourceY.end, sourceY.start, SURFBY.start);
|
||||
snaps |= SNAP_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// corner snapping
|
||||
if (sourceX.start == SURFBX.end || SURFBX.start == sourceX.end) {
|
||||
const SRange SURFY = {SURFBY.start + GAPSY, SURFBY.end - GAPSY};
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && !(snaps & SNAP_UP) && canSnap(sourceY.start, SURFY.start, GAPSIZE)) {
|
||||
SNAP(sourceY.start, sourceY.end, SURFY.start);
|
||||
snaps |= SNAP_UP;
|
||||
} else if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_DOWN) && canSnap(sourceY.end, SURFY.end, GAPSIZE)) {
|
||||
SNAP(sourceY.end, sourceY.start, SURFY.end);
|
||||
snaps |= SNAP_DOWN;
|
||||
}
|
||||
}
|
||||
if (sourceY.start == SURFBY.end || SURFBY.start == sourceY.end) {
|
||||
const SRange SURFX = {SURFBX.start + GAPSX, SURFBX.end - GAPSX};
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && !(snaps & SNAP_LEFT) && canSnap(sourceX.start, SURFX.start, GAPSIZE)) {
|
||||
SNAP(sourceX.start, sourceX.end, SURFX.start);
|
||||
snaps |= SNAP_LEFT;
|
||||
} else if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && !(snaps & SNAP_RIGHT) && canSnap(sourceX.end, SURFX.end, GAPSIZE)) {
|
||||
SNAP(sourceX.end, sourceX.start, SURFX.end);
|
||||
snaps |= SNAP_RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (*SNAPMONITORGAP) {
|
||||
const double GAPSIZE = *SNAPMONITORGAP;
|
||||
const auto EXTENTNONE = SBoxExtents{{0, 0}, {0, 0}};
|
||||
const auto* EXTENTDIFF = *SNAPBORDEROVERLAP ? &EXTENTS : &EXTENTNONE;
|
||||
const auto MON = DRAGGINGWINDOW->m_monitor.lock();
|
||||
|
||||
const auto* GAPSOUT = *SNAPRESPECTGAPS ? sc<CCssGapData*>(PGAPSOUT.ptr()->getData()) : &GAPSNONE;
|
||||
const auto WORK_AREA = Desktop::CReservedArea{GAPSOUT->m_top, GAPSOUT->m_right, GAPSOUT->m_bottom, GAPSOUT->m_left}.apply(MON->logicalBoxMinusReserved());
|
||||
|
||||
SRange monX = {WORK_AREA.x, WORK_AREA.x + WORK_AREA.w};
|
||||
SRange monY = {WORK_AREA.y, WORK_AREA.y + WORK_AREA.h};
|
||||
|
||||
const bool HAS_LEFT = MON->m_reservedArea.left() > 0;
|
||||
const bool HAS_TOP = MON->m_reservedArea.top() > 0;
|
||||
const bool HAS_BOTTOM = MON->m_reservedArea.bottom() > 0;
|
||||
const bool HAS_RIGHT = MON->m_reservedArea.right() > 0;
|
||||
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) &&
|
||||
((HAS_LEFT && canSnap(sourceX.start, monX.start, GAPSIZE)) || canSnap(sourceX.start, (monX.start -= MON->m_reservedArea.left() + EXTENTDIFF->topLeft.x), GAPSIZE))) {
|
||||
SNAP(sourceX.start, sourceX.end, monX.start);
|
||||
snaps |= SNAP_LEFT;
|
||||
}
|
||||
if (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) &&
|
||||
((HAS_RIGHT && canSnap(sourceX.end, monX.end, GAPSIZE)) || canSnap(sourceX.end, (monX.end += MON->m_reservedArea.right() + EXTENTDIFF->bottomRight.x), GAPSIZE))) {
|
||||
SNAP(sourceX.end, sourceX.start, monX.end);
|
||||
snaps |= SNAP_RIGHT;
|
||||
}
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) &&
|
||||
((HAS_TOP && canSnap(sourceY.start, monY.start, GAPSIZE)) || canSnap(sourceY.start, (monY.start -= MON->m_reservedArea.top() + EXTENTDIFF->topLeft.y), GAPSIZE))) {
|
||||
SNAP(sourceY.start, sourceY.end, monY.start);
|
||||
snaps |= SNAP_UP;
|
||||
}
|
||||
if (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) &&
|
||||
((HAS_BOTTOM && canSnap(sourceY.end, monY.end, GAPSIZE)) || canSnap(sourceY.end, (monY.end += MON->m_reservedArea.bottom() + EXTENTDIFF->bottomRight.y), GAPSIZE))) {
|
||||
SNAP(sourceY.end, sourceY.start, monY.end);
|
||||
snaps |= SNAP_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
// remove extents from main surface
|
||||
sourceX = {sourceX.start + EXTENTS.topLeft.x, sourceX.end - EXTENTS.bottomRight.x};
|
||||
sourceY = {sourceY.start + EXTENTS.topLeft.y, sourceY.end - EXTENTS.bottomRight.y};
|
||||
|
||||
if (MODE == MBIND_RESIZE_FORCE_RATIO) {
|
||||
if ((CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT) && snaps & SNAP_LEFT) || (CORNER & (CORNER_TOPRIGHT | CORNER_BOTTOMRIGHT) && snaps & SNAP_RIGHT)) {
|
||||
const double SIZEY = (sourceX.end - sourceX.start) * (BEGINSIZE.y / BEGINSIZE.x);
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT))
|
||||
sourceY.start = sourceY.end - SIZEY;
|
||||
else
|
||||
sourceY.end = sourceY.start + SIZEY;
|
||||
} else if ((CORNER & (CORNER_TOPLEFT | CORNER_TOPRIGHT) && snaps & SNAP_UP) || (CORNER & (CORNER_BOTTOMLEFT | CORNER_BOTTOMRIGHT) && snaps & SNAP_DOWN)) {
|
||||
const double SIZEX = (sourceY.end - sourceY.start) * (BEGINSIZE.x / BEGINSIZE.y);
|
||||
if (CORNER & (CORNER_TOPLEFT | CORNER_BOTTOMLEFT))
|
||||
sourceX.start = sourceX.end - SIZEX;
|
||||
else
|
||||
sourceX.end = sourceX.start + SIZEX;
|
||||
}
|
||||
}
|
||||
|
||||
sourcePos = {sourceX.start, sourceY.start};
|
||||
sourceSize = {sourceX.end - sourceX.start, sourceY.end - sourceY.start};
|
||||
}
|
||||
|
||||
void CLayoutManager::recalculateMonitor(PHLMONITOR m) {
|
||||
if (m->m_activeSpecialWorkspace)
|
||||
m->m_activeSpecialWorkspace->m_space->recalculate();
|
||||
if (m->m_activeWorkspace)
|
||||
m->m_activeWorkspace->m_space->recalculate();
|
||||
}
|
||||
|
||||
void CLayoutManager::invalidateMonitorGeometries(PHLMONITOR m) {
|
||||
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||
if (ws && ws->m_monitor == m) {
|
||||
ws->m_space->recheckWorkArea();
|
||||
ws->m_space->recalculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/layout/LayoutManager.hpp
Normal file
86
src/layout/LayoutManager.hpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#pragma once
|
||||
|
||||
#include "../helpers/memory/Memory.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
|
||||
#include "supplementary/DragController.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <expected>
|
||||
|
||||
enum eFullscreenMode : int8_t;
|
||||
|
||||
namespace Layout {
|
||||
class ITarget;
|
||||
class CSpace;
|
||||
|
||||
enum eRectCorner : uint8_t {
|
||||
CORNER_NONE = 0,
|
||||
CORNER_TOPLEFT = (1 << 0),
|
||||
CORNER_TOPRIGHT = (1 << 1),
|
||||
CORNER_BOTTOMRIGHT = (1 << 2),
|
||||
CORNER_BOTTOMLEFT = (1 << 3),
|
||||
};
|
||||
|
||||
inline eRectCorner cornerFromBox(const CBox& box, const Vector2D& pos) {
|
||||
const auto CENTER = box.middle();
|
||||
|
||||
if (pos.x < CENTER.x)
|
||||
return pos.y < CENTER.y ? CORNER_TOPLEFT : CORNER_BOTTOMLEFT;
|
||||
return pos.y < CENTER.y ? CORNER_TOPRIGHT : CORNER_BOTTOMRIGHT;
|
||||
}
|
||||
|
||||
enum eSnapEdge : uint8_t {
|
||||
SNAP_INVALID = 0,
|
||||
SNAP_UP = (1 << 0),
|
||||
SNAP_DOWN = (1 << 1),
|
||||
SNAP_LEFT = (1 << 2),
|
||||
SNAP_RIGHT = (1 << 3),
|
||||
};
|
||||
|
||||
class CLayoutManager {
|
||||
public:
|
||||
CLayoutManager();
|
||||
~CLayoutManager() = default;
|
||||
|
||||
void newTarget(SP<ITarget> target, SP<CSpace> space);
|
||||
void removeTarget(SP<ITarget> target);
|
||||
|
||||
void changeFloatingMode(SP<ITarget> target);
|
||||
|
||||
void beginDragTarget(SP<ITarget> target, eMouseBindMode mode);
|
||||
void moveMouse(const Vector2D& mousePos);
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
void endDragTarget();
|
||||
|
||||
std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
|
||||
void fullscreenRequestForTarget(SP<ITarget> target, eFullscreenMode currentEffectiveMode, eFullscreenMode effectiveMode);
|
||||
|
||||
void switchTargets(SP<ITarget> a, SP<ITarget> b, bool preserveFocus = true);
|
||||
|
||||
void moveInDirection(SP<ITarget> target, const std::string& direction, bool silent = false);
|
||||
|
||||
SP<ITarget> getNextCandidate(SP<CSpace> space, SP<ITarget> from);
|
||||
|
||||
bool isReachable(SP<ITarget> target);
|
||||
|
||||
void bringTargetToTop(SP<ITarget> target);
|
||||
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
|
||||
void performSnap(Vector2D& sourcePos, Vector2D& sourceSize, SP<ITarget> target, eMouseBindMode mode, int corner, const Vector2D& beginSize);
|
||||
|
||||
void invalidateMonitorGeometries(PHLMONITOR);
|
||||
void recalculateMonitor(PHLMONITOR);
|
||||
|
||||
const UP<Supplementary::CDragStateController>& dragController();
|
||||
|
||||
private:
|
||||
UP<Supplementary::CDragStateController> m_dragStateController = makeUnique<Supplementary::CDragStateController>();
|
||||
};
|
||||
}
|
||||
|
||||
inline UP<Layout::CLayoutManager> g_layoutManager;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,112 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "IHyprLayout.hpp"
|
||||
#include "../desktop/DesktopTypes.hpp"
|
||||
#include "../helpers/varlist/VarList.hpp"
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <any>
|
||||
|
||||
enum eFullscreenMode : int8_t;
|
||||
|
||||
//orientation determines which side of the screen the master area resides
|
||||
enum eOrientation : uint8_t {
|
||||
ORIENTATION_LEFT = 0,
|
||||
ORIENTATION_TOP,
|
||||
ORIENTATION_RIGHT,
|
||||
ORIENTATION_BOTTOM,
|
||||
ORIENTATION_CENTER
|
||||
};
|
||||
|
||||
struct SMasterNodeData {
|
||||
bool isMaster = false;
|
||||
float percMaster = 0.5f;
|
||||
|
||||
PHLWINDOWREF pWindow;
|
||||
|
||||
Vector2D position;
|
||||
Vector2D size;
|
||||
|
||||
float percSize = 1.f; // size multiplier for resizing children
|
||||
|
||||
WORKSPACEID workspaceID = WORKSPACE_INVALID;
|
||||
|
||||
bool ignoreFullscreenChecks = false;
|
||||
|
||||
//
|
||||
bool operator==(const SMasterNodeData& rhs) const {
|
||||
return pWindow.lock() == rhs.pWindow.lock();
|
||||
}
|
||||
};
|
||||
|
||||
struct SMasterWorkspaceData {
|
||||
WORKSPACEID workspaceID = WORKSPACE_INVALID;
|
||||
eOrientation orientation = ORIENTATION_LEFT;
|
||||
// Previously focused non-master window when `focusmaster previous` command was issued
|
||||
PHLWINDOWREF focusMasterPrev;
|
||||
|
||||
//
|
||||
bool operator==(const SMasterWorkspaceData& rhs) const {
|
||||
return workspaceID == rhs.workspaceID;
|
||||
}
|
||||
};
|
||||
|
||||
class CHyprMasterLayout : public IHyprLayout {
|
||||
public:
|
||||
virtual void onWindowCreatedTiling(PHLWINDOW, eDirection direction = DIRECTION_DEFAULT);
|
||||
virtual void onWindowRemovedTiling(PHLWINDOW);
|
||||
virtual bool isWindowTiled(PHLWINDOW);
|
||||
virtual void recalculateMonitor(const MONITORID&);
|
||||
virtual void recalculateWindow(PHLWINDOW);
|
||||
virtual void resizeActiveWindow(const Vector2D&, eRectCorner corner = CORNER_NONE, PHLWINDOW pWindow = nullptr);
|
||||
virtual void fullscreenRequestForWindow(PHLWINDOW pWindow, const eFullscreenMode CURRENT_EFFECTIVE_MODE, const eFullscreenMode EFFECTIVE_MODE);
|
||||
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
|
||||
virtual SWindowRenderLayoutHints requestRenderHints(PHLWINDOW);
|
||||
virtual void switchWindows(PHLWINDOW, PHLWINDOW);
|
||||
virtual void moveWindowTo(PHLWINDOW, const std::string& dir, bool silent);
|
||||
virtual void alterSplitRatio(PHLWINDOW, float, bool);
|
||||
virtual std::string getLayoutName();
|
||||
virtual void replaceWindowDataWith(PHLWINDOW from, PHLWINDOW to);
|
||||
virtual Vector2D predictSizeForNewWindowTiled();
|
||||
|
||||
virtual void onEnable();
|
||||
virtual void onDisable();
|
||||
|
||||
private:
|
||||
std::list<SMasterNodeData> m_masterNodesData;
|
||||
std::vector<SMasterWorkspaceData> m_masterWorkspacesData;
|
||||
|
||||
bool m_forceWarps = false;
|
||||
|
||||
void buildOrientationCycleVectorFromVars(std::vector<eOrientation>& cycle, CVarList& vars);
|
||||
void buildOrientationCycleVectorFromEOperation(std::vector<eOrientation>& cycle);
|
||||
void runOrientationCycle(SLayoutMessageHeader& header, CVarList* vars, int next);
|
||||
eOrientation getDynamicOrientation(PHLWORKSPACE);
|
||||
int getNodesOnWorkspace(const WORKSPACEID&);
|
||||
void applyNodeDataToWindow(SMasterNodeData*);
|
||||
SMasterNodeData* getNodeFromWindow(PHLWINDOW);
|
||||
SMasterNodeData* getMasterNodeOnWorkspace(const WORKSPACEID&);
|
||||
SMasterWorkspaceData* getMasterWorkspaceData(const WORKSPACEID&);
|
||||
void calculateWorkspace(PHLWORKSPACE);
|
||||
PHLWINDOW getNextWindow(PHLWINDOW, bool, bool);
|
||||
int getMastersOnWorkspace(const WORKSPACEID&);
|
||||
|
||||
friend struct SMasterNodeData;
|
||||
friend struct SMasterWorkspaceData;
|
||||
};
|
||||
|
||||
template <typename CharT>
|
||||
struct std::formatter<SMasterNodeData*, CharT> : std::formatter<CharT> {
|
||||
template <typename FormatContext>
|
||||
auto format(const SMasterNodeData* const& node, FormatContext& ctx) const {
|
||||
auto out = ctx.out();
|
||||
if (!node)
|
||||
return std::format_to(out, "[Node nullptr]");
|
||||
std::format_to(out, "[Node {:x}: workspace: {}, pos: {:j2}, size: {:j2}", rc<uintptr_t>(node), node->workspaceID, node->position, node->size);
|
||||
if (node->isMaster)
|
||||
std::format_to(out, ", master");
|
||||
if (!node->pWindow.expired())
|
||||
std::format_to(out, ", window: {:x}", node->pWindow.lock());
|
||||
return std::format_to(out, "]");
|
||||
}
|
||||
};
|
||||
264
src/layout/algorithm/Algorithm.cpp
Normal file
264
src/layout/algorithm/Algorithm.cpp
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
#include "Algorithm.hpp"
|
||||
|
||||
#include "FloatingAlgorithm.hpp"
|
||||
#include "TiledAlgorithm.hpp"
|
||||
#include "../target/WindowTarget.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
#include "../../desktop/history/WindowHistoryTracker.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
#include "../../debug/log/Logger.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<CAlgorithm> CAlgorithm::create(UP<ITiledAlgorithm>&& tiled, UP<IFloatingAlgorithm>&& floating, SP<CSpace> space) {
|
||||
auto algo = SP<CAlgorithm>(new CAlgorithm(std::move(tiled), std::move(floating), space));
|
||||
algo->m_self = algo;
|
||||
algo->m_tiled->m_parent = algo;
|
||||
algo->m_floating->m_parent = algo;
|
||||
return algo;
|
||||
}
|
||||
|
||||
CAlgorithm::CAlgorithm(UP<ITiledAlgorithm>&& tiled, UP<IFloatingAlgorithm>&& floating, SP<CSpace> space) :
|
||||
m_tiled(std::move(tiled)), m_floating(std::move(floating)), m_space(space) {
|
||||
;
|
||||
}
|
||||
|
||||
void CAlgorithm::addTarget(SP<ITarget> target) {
|
||||
const bool SHOULD_FLOAT = target->floating();
|
||||
|
||||
if (SHOULD_FLOAT) {
|
||||
m_floatingTargets.emplace_back(target);
|
||||
m_floating->newTarget(target);
|
||||
} else {
|
||||
m_tiledTargets.emplace_back(target);
|
||||
m_tiled->newTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
void CAlgorithm::removeTarget(SP<ITarget> target) {
|
||||
const bool IS_FLOATING = std::ranges::contains(m_floatingTargets, target);
|
||||
|
||||
if (IS_FLOATING) {
|
||||
m_floating->removeTarget(target);
|
||||
std::erase(m_floatingTargets, target);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool IS_TILED = std::ranges::contains(m_tiledTargets, target);
|
||||
|
||||
if (IS_TILED) {
|
||||
m_tiled->removeTarget(target);
|
||||
std::erase(m_tiledTargets, target);
|
||||
return;
|
||||
}
|
||||
|
||||
Log::logger->log(Log::ERR, "BUG THIS: CAlgorithm::removeTarget, but not found");
|
||||
}
|
||||
|
||||
void CAlgorithm::moveTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint, bool reposition) {
|
||||
const bool SHOULD_FLOAT = target->floating();
|
||||
|
||||
if (SHOULD_FLOAT) {
|
||||
m_floatingTargets.emplace_back(target);
|
||||
if (reposition)
|
||||
m_floating->newTarget(target);
|
||||
else
|
||||
m_floating->movedTarget(target, focalPoint);
|
||||
} else {
|
||||
m_tiledTargets.emplace_back(target);
|
||||
if (reposition)
|
||||
m_tiled->newTarget(target);
|
||||
else
|
||||
m_tiled->movedTarget(target, focalPoint);
|
||||
}
|
||||
}
|
||||
|
||||
SP<CSpace> CAlgorithm::space() const {
|
||||
return m_space.lock();
|
||||
}
|
||||
|
||||
void CAlgorithm::setFloating(SP<ITarget> target, bool floating, bool reposition) {
|
||||
removeTarget(target);
|
||||
|
||||
g_pHyprRenderer->damageWindow(target->window());
|
||||
|
||||
target->setFloating(floating);
|
||||
|
||||
moveTarget(target, std::nullopt, reposition);
|
||||
|
||||
g_pHyprRenderer->damageWindow(target->window());
|
||||
}
|
||||
|
||||
size_t CAlgorithm::tiledTargets() const {
|
||||
return m_tiledTargets.size();
|
||||
}
|
||||
|
||||
size_t CAlgorithm::floatingTargets() const {
|
||||
return m_floatingTargets.size();
|
||||
}
|
||||
|
||||
void CAlgorithm::recalculate() {
|
||||
m_tiled->recalculate();
|
||||
m_floating->recalculate();
|
||||
|
||||
const auto PWORKSPACE = m_space->workspace();
|
||||
const auto PMONITOR = PWORKSPACE->m_monitor;
|
||||
|
||||
if (PWORKSPACE->m_hasFullscreenWindow && PMONITOR) {
|
||||
// massive hack from the fullscreen func
|
||||
const auto PFULLWINDOW = PWORKSPACE->getFullscreenWindow();
|
||||
|
||||
if (PFULLWINDOW) {
|
||||
if (PWORKSPACE->m_fullscreenMode == FSMODE_FULLSCREEN) {
|
||||
*PFULLWINDOW->m_realPosition = PMONITOR->m_position;
|
||||
*PFULLWINDOW->m_realSize = PMONITOR->m_size;
|
||||
} else if (PWORKSPACE->m_fullscreenMode == FSMODE_MAXIMIZED)
|
||||
PFULLWINDOW->layoutTarget()->setPositionGlobal(m_space->workArea());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CAlgorithm::recenter(SP<ITarget> t) {
|
||||
if (t->floating())
|
||||
m_floating->recenter(t);
|
||||
}
|
||||
|
||||
std::expected<void, std::string> CAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||
if (const auto ret = m_floating->layoutMsg(sv); !ret)
|
||||
return ret;
|
||||
return m_tiled->layoutMsg(sv);
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CAlgorithm::predictSizeForNewTiledTarget() {
|
||||
return m_tiled->predictSizeForNewTarget();
|
||||
}
|
||||
|
||||
void CAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
if (target->floating())
|
||||
m_floating->resizeTarget(Δ, target, corner);
|
||||
else
|
||||
m_tiled->resizeTarget(Δ, target, corner);
|
||||
}
|
||||
|
||||
void CAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
||||
if (target->floating())
|
||||
m_floating->moveTarget(Δ, target);
|
||||
}
|
||||
|
||||
void CAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||
auto swapFirst = [&a, &b](std::vector<WP<ITarget>>& targets) -> bool {
|
||||
auto ia = std::ranges::find(targets, a);
|
||||
auto ib = std::ranges::find(targets, b);
|
||||
|
||||
if (ia != std::ranges::end(targets) && ib != std::ranges::end(targets)) {
|
||||
std::iter_swap(ia, ib);
|
||||
return true;
|
||||
} else if (ia != std::ranges::end(targets))
|
||||
*ia = b;
|
||||
else if (ib != std::ranges::end(targets))
|
||||
*ib = a;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!swapFirst(m_tiledTargets))
|
||||
swapFirst(m_floatingTargets);
|
||||
|
||||
const WP<IModeAlgorithm> algA = a->floating() ? WP<IModeAlgorithm>(m_floating) : WP<IModeAlgorithm>(m_tiled);
|
||||
const WP<IModeAlgorithm> algB = b->floating() ? WP<IModeAlgorithm>(m_floating) : WP<IModeAlgorithm>(m_tiled);
|
||||
|
||||
algA->swapTargets(a, b);
|
||||
if (algA != algB)
|
||||
algB->swapTargets(b, a);
|
||||
}
|
||||
|
||||
void CAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
if (t->floating())
|
||||
m_floating->moveTargetInDirection(t, dir, silent);
|
||||
else
|
||||
m_tiled->moveTargetInDirection(t, dir, silent);
|
||||
}
|
||||
|
||||
void CAlgorithm::updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo) {
|
||||
algo->m_parent = m_self;
|
||||
|
||||
for (const auto& t : m_floatingTargets) {
|
||||
const auto TARGET = t.lock();
|
||||
if (!TARGET)
|
||||
continue;
|
||||
|
||||
// Unhide windows when switching layouts to prevent them from being permanently lost
|
||||
const auto WINDOW = TARGET->window();
|
||||
if (WINDOW)
|
||||
WINDOW->setHidden(false);
|
||||
|
||||
m_floating->removeTarget(TARGET);
|
||||
algo->newTarget(TARGET);
|
||||
}
|
||||
|
||||
m_floating = std::move(algo);
|
||||
}
|
||||
|
||||
void CAlgorithm::updateTiledAlgo(UP<ITiledAlgorithm>&& algo) {
|
||||
algo->m_parent = m_self;
|
||||
|
||||
for (const auto& t : m_tiledTargets) {
|
||||
const auto TARGET = t.lock();
|
||||
if (!TARGET)
|
||||
continue;
|
||||
|
||||
// Unhide windows when switching layouts to prevent them from being permanently lost
|
||||
// This is a safeguard for layouts (including third-party plugins) that use setHidden
|
||||
const auto WINDOW = TARGET->window();
|
||||
if (WINDOW)
|
||||
WINDOW->setHidden(false);
|
||||
|
||||
m_tiled->removeTarget(TARGET);
|
||||
algo->newTarget(TARGET);
|
||||
}
|
||||
|
||||
m_tiled = std::move(algo);
|
||||
}
|
||||
|
||||
const UP<ITiledAlgorithm>& CAlgorithm::tiledAlgo() const {
|
||||
return m_tiled;
|
||||
}
|
||||
|
||||
const UP<IFloatingAlgorithm>& CAlgorithm::floatingAlgo() const {
|
||||
return m_floating;
|
||||
}
|
||||
|
||||
SP<ITarget> CAlgorithm::getNextCandidate(SP<ITarget> old) {
|
||||
if (old->floating()) {
|
||||
// use window history to determine best target
|
||||
for (const auto& w : Desktop::History::windowTracker()->fullHistory() | std::views::reverse) {
|
||||
if (!w->m_workspace || w->m_workspace->m_space != m_space || !w->layoutTarget() || !w->layoutTarget()->space())
|
||||
continue;
|
||||
|
||||
return w->layoutTarget();
|
||||
}
|
||||
|
||||
// no history, fall back
|
||||
} else {
|
||||
// ask the layout
|
||||
const auto CANDIDATE = m_tiled->getNextCandidate(old);
|
||||
if (CANDIDATE)
|
||||
return CANDIDATE;
|
||||
|
||||
// no candidate, fall back
|
||||
}
|
||||
|
||||
// fallback: try to focus anything
|
||||
if (!m_tiledTargets.empty())
|
||||
return m_tiledTargets.back().lock();
|
||||
if (!m_floatingTargets.empty())
|
||||
return m_floatingTargets.back().lock();
|
||||
|
||||
// god damn it, maybe empty?
|
||||
return nullptr;
|
||||
}
|
||||
64
src/layout/algorithm/Algorithm.hpp
Normal file
64
src/layout/algorithm/Algorithm.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/math/Direction.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "../LayoutManager.hpp"
|
||||
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
|
||||
namespace Layout {
|
||||
class ITarget;
|
||||
class IFloatingAlgorithm;
|
||||
class ITiledAlgorithm;
|
||||
class CSpace;
|
||||
|
||||
class CAlgorithm {
|
||||
public:
|
||||
static SP<CAlgorithm> create(UP<ITiledAlgorithm>&& tiled, UP<IFloatingAlgorithm>&& floating, SP<CSpace> space);
|
||||
~CAlgorithm() = default;
|
||||
|
||||
void addTarget(SP<ITarget> target);
|
||||
void moveTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt, bool reposition = false);
|
||||
void removeTarget(SP<ITarget> target);
|
||||
|
||||
void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
void setFloating(SP<ITarget> target, bool floating, bool reposition = false);
|
||||
|
||||
std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
|
||||
void recalculate();
|
||||
void recenter(SP<ITarget> t);
|
||||
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
|
||||
void updateFloatingAlgo(UP<IFloatingAlgorithm>&& algo);
|
||||
void updateTiledAlgo(UP<ITiledAlgorithm>&& algo);
|
||||
|
||||
const UP<ITiledAlgorithm>& tiledAlgo() const;
|
||||
const UP<IFloatingAlgorithm>& floatingAlgo() const;
|
||||
|
||||
SP<CSpace> space() const;
|
||||
|
||||
size_t tiledTargets() const;
|
||||
size_t floatingTargets() const;
|
||||
|
||||
private:
|
||||
CAlgorithm(UP<ITiledAlgorithm>&& tiled, UP<IFloatingAlgorithm>&& floating, SP<CSpace> space);
|
||||
|
||||
UP<ITiledAlgorithm> m_tiled;
|
||||
UP<IFloatingAlgorithm> m_floating;
|
||||
WP<CSpace> m_space;
|
||||
WP<CAlgorithm> m_self;
|
||||
|
||||
std::vector<WP<ITarget>> m_tiledTargets, m_floatingTargets;
|
||||
};
|
||||
}
|
||||
18
src/layout/algorithm/FloatingAlgorithm.cpp
Normal file
18
src/layout/algorithm/FloatingAlgorithm.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include "FloatingAlgorithm.hpp"
|
||||
#include "Algorithm.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
void IFloatingAlgorithm::recalculate() {
|
||||
;
|
||||
}
|
||||
|
||||
void IFloatingAlgorithm::recenter(SP<ITarget> t) {
|
||||
const auto LAST = t->lastFloatingSize();
|
||||
|
||||
if (LAST.x <= 5 || LAST.y <= 5)
|
||||
return;
|
||||
|
||||
t->setPositionGlobal({m_parent->space()->workArea().middle() - LAST / 2.F, LAST});
|
||||
}
|
||||
31
src/layout/algorithm/FloatingAlgorithm.hpp
Normal file
31
src/layout/algorithm/FloatingAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "ModeAlgorithm.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
class IFloatingAlgorithm : public IModeAlgorithm {
|
||||
public:
|
||||
virtual ~IFloatingAlgorithm() = default;
|
||||
|
||||
// a target is being moved by a delta
|
||||
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target) = 0;
|
||||
|
||||
virtual void recenter(SP<ITarget> t);
|
||||
|
||||
virtual void recalculate();
|
||||
|
||||
protected:
|
||||
IFloatingAlgorithm() = default;
|
||||
|
||||
WP<CAlgorithm> m_parent;
|
||||
|
||||
friend class Layout::CAlgorithm;
|
||||
};
|
||||
}
|
||||
11
src/layout/algorithm/ModeAlgorithm.cpp
Normal file
11
src/layout/algorithm/ModeAlgorithm.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "ModeAlgorithm.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
std::expected<void, std::string> IModeAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Vector2D> IModeAlgorithm::predictSizeForNewTarget() {
|
||||
return std::nullopt;
|
||||
}
|
||||
54
src/layout/algorithm/ModeAlgorithm.hpp
Normal file
54
src/layout/algorithm/ModeAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/math/Direction.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "../LayoutManager.hpp"
|
||||
|
||||
#include <expected>
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
class IModeAlgorithm {
|
||||
public:
|
||||
virtual ~IModeAlgorithm() = default;
|
||||
|
||||
// a completely new target
|
||||
virtual void newTarget(SP<ITarget> target) = 0;
|
||||
|
||||
// a target moved into the algorithm (from another)
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt) = 0;
|
||||
|
||||
// a target removed
|
||||
virtual void removeTarget(SP<ITarget> target) = 0;
|
||||
|
||||
// a target is being resized by a delta. Corner none likely means not interactive
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE) = 0;
|
||||
|
||||
// recalculate layout
|
||||
virtual void recalculate() = 0;
|
||||
|
||||
// swap targets
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b) = 0;
|
||||
|
||||
// move a target in a given direction
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) = 0;
|
||||
|
||||
// optional: handle layout messages
|
||||
virtual std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
|
||||
// optional: predict new window's size
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
protected:
|
||||
IModeAlgorithm() = default;
|
||||
|
||||
WP<CAlgorithm> m_parent;
|
||||
|
||||
friend class Layout::CAlgorithm;
|
||||
};
|
||||
}
|
||||
26
src/layout/algorithm/TiledAlgorithm.hpp
Normal file
26
src/layout/algorithm/TiledAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "ModeAlgorithm.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
class ITiledAlgorithm : public IModeAlgorithm {
|
||||
public:
|
||||
virtual ~ITiledAlgorithm() = default;
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old) = 0;
|
||||
|
||||
protected:
|
||||
ITiledAlgorithm() = default;
|
||||
|
||||
WP<CAlgorithm> m_parent;
|
||||
|
||||
friend class Layout::CAlgorithm;
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
#include "DefaultFloatingAlgorithm.hpp"
|
||||
|
||||
#include "../../Algorithm.hpp"
|
||||
|
||||
#include "../../../target/WindowTarget.hpp"
|
||||
#include "../../../space/Space.hpp"
|
||||
|
||||
#include "../../../../Compositor.hpp"
|
||||
#include "../../../../helpers/Monitor.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Layout::Floating;
|
||||
|
||||
constexpr const Vector2D DEFAULT_SIZE = {640, 400};
|
||||
|
||||
//
|
||||
void CDefaultFloatingAlgorithm::newTarget(SP<ITarget> target) {
|
||||
const auto WORK_AREA = m_parent->space()->workArea(true);
|
||||
const auto DESIRED_GEOM = target->desiredGeometry();
|
||||
const auto MONITOR_POS = m_parent->space()->workspace()->m_monitor->logicalBox().pos();
|
||||
|
||||
CBox windowGeometry;
|
||||
|
||||
if (!DESIRED_GEOM) {
|
||||
switch (DESIRED_GEOM.error()) {
|
||||
case GEOMETRY_INVALID_DESIRED: {
|
||||
// if the desired is invalid, we hide the window.
|
||||
if (target->type() == TARGET_TYPE_WINDOW)
|
||||
dynamicPointerCast<CWindowTarget>(target)->window()->setHidden(true);
|
||||
return;
|
||||
}
|
||||
case GEOMETRY_NO_DESIRED: {
|
||||
// add a default geom
|
||||
windowGeometry = CBox{WORK_AREA.middle() - DEFAULT_SIZE / 2.F, DEFAULT_SIZE};
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (DESIRED_GEOM->pos)
|
||||
windowGeometry = CBox{DESIRED_GEOM->pos.value(), DESIRED_GEOM->size};
|
||||
else
|
||||
windowGeometry = CBox{WORK_AREA.middle() - DESIRED_GEOM->size / 2.F, DESIRED_GEOM->size};
|
||||
}
|
||||
|
||||
bool posOverridden = false;
|
||||
|
||||
if (target->window() && target->window()->m_firstMap) {
|
||||
const auto WINDOW = target->window();
|
||||
|
||||
// set this here so that expressions can use it. This could be wrong of course.
|
||||
WINDOW->m_realSize->setValueAndWarp(DESIRED_GEOM ? DESIRED_GEOM->size : DEFAULT_SIZE);
|
||||
|
||||
if (!WINDOW->m_ruleApplicator->static_.size.empty()) {
|
||||
const auto COMPUTED = WINDOW->calculateExpression(WINDOW->m_ruleApplicator->static_.size);
|
||||
if (!COMPUTED)
|
||||
Log::logger->log(Log::ERR, "failed to parse {} as an expression", WINDOW->m_ruleApplicator->static_.size);
|
||||
else {
|
||||
windowGeometry.w = COMPUTED->x;
|
||||
windowGeometry.h = COMPUTED->y;
|
||||
|
||||
// update for pos to work with size.
|
||||
WINDOW->m_realPosition->setValueAndWarp(*COMPUTED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!WINDOW->m_ruleApplicator->static_.position.empty()) {
|
||||
const auto COMPUTED = WINDOW->calculateExpression(WINDOW->m_ruleApplicator->static_.position);
|
||||
if (!COMPUTED)
|
||||
Log::logger->log(Log::ERR, "failed to parse {} as an expression", WINDOW->m_ruleApplicator->static_.position);
|
||||
else {
|
||||
windowGeometry.x = COMPUTED->x + MONITOR_POS.x;
|
||||
windowGeometry.y = COMPUTED->y + MONITOR_POS.y;
|
||||
posOverridden = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (WINDOW->m_ruleApplicator->static_.center.value_or(false)) {
|
||||
const auto POS = WORK_AREA.middle() - windowGeometry.size() / 2.f;
|
||||
windowGeometry.x = POS.x;
|
||||
windowGeometry.y = POS.y;
|
||||
posOverridden = true;
|
||||
}
|
||||
} else if (target->lastFloatingSize().x > 5 && target->lastFloatingSize().y > 5) {
|
||||
windowGeometry.w = target->lastFloatingSize().x;
|
||||
windowGeometry.h = target->lastFloatingSize().y;
|
||||
}
|
||||
|
||||
if (!posOverridden && (!DESIRED_GEOM || !DESIRED_GEOM->pos))
|
||||
windowGeometry = CBox{WORK_AREA.middle() - windowGeometry.size() / 2.F, windowGeometry.size()};
|
||||
|
||||
if (posOverridden || WORK_AREA.containsPoint(windowGeometry.middle()))
|
||||
target->setPositionGlobal(windowGeometry);
|
||||
else {
|
||||
const auto POS = WORK_AREA.middle() - windowGeometry.size() / 2.f;
|
||||
windowGeometry.x = POS.x;
|
||||
windowGeometry.y = POS.y;
|
||||
|
||||
target->setPositionGlobal(windowGeometry);
|
||||
}
|
||||
|
||||
// TODO: not very OOP, is it?
|
||||
if (const auto WTARGET = dynamicPointerCast<CWindowTarget>(target); WTARGET) {
|
||||
static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
|
||||
|
||||
const auto PWINDOW = WTARGET->window();
|
||||
const auto PMONITOR = WTARGET->space()->workspace()->m_monitor.lock();
|
||||
|
||||
if (*PXWLFORCESCALEZERO && PWINDOW->m_isX11)
|
||||
*PWINDOW->m_realSize = PWINDOW->m_realSize->goal() / PMONITOR->m_scale;
|
||||
|
||||
if (PWINDOW->m_X11DoesntWantBorders || (PWINDOW->m_isX11 && PWINDOW->isX11OverrideRedirect())) {
|
||||
PWINDOW->m_realPosition->warp();
|
||||
PWINDOW->m_realSize->warp();
|
||||
}
|
||||
|
||||
if (!PWINDOW->isX11OverrideRedirect())
|
||||
g_pCompositor->changeWindowZOrder(PWINDOW, true);
|
||||
else {
|
||||
PWINDOW->m_pendingReportedSize = PWINDOW->m_realSize->goal();
|
||||
PWINDOW->m_reportedSize = PWINDOW->m_pendingReportedSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint) {
|
||||
auto LAST_SIZE = target->lastFloatingSize();
|
||||
const auto CURRENT_SIZE = target->position().size();
|
||||
|
||||
// ignore positioning a dragged target
|
||||
if (g_layoutManager->dragController()->target() == target)
|
||||
return;
|
||||
|
||||
if (LAST_SIZE.x < 5 || LAST_SIZE.y < 5) {
|
||||
const auto DESIRED = target->desiredGeometry();
|
||||
LAST_SIZE = DESIRED ? DESIRED->size : DEFAULT_SIZE;
|
||||
}
|
||||
|
||||
if (target->wasTiling()) {
|
||||
// Avoid floating toggles that don't change size, they aren't easily visible to the user
|
||||
if (std::abs(LAST_SIZE.x - CURRENT_SIZE.x) < 5 && std::abs(LAST_SIZE.y - CURRENT_SIZE.y) < 5)
|
||||
LAST_SIZE += Vector2D{10, 10};
|
||||
|
||||
// calculate new position
|
||||
const auto OLD_CENTER = target->position().middle();
|
||||
|
||||
// put around the current center, fit in workArea
|
||||
target->setPositionGlobal(fitBoxInWorkArea(CBox{OLD_CENTER - LAST_SIZE / 2.F, LAST_SIZE}, target));
|
||||
|
||||
} else {
|
||||
// calculate new position
|
||||
const auto THIS_MON_POS = m_parent->space()->workspace()->m_monitor->m_position;
|
||||
const auto OLD_POS = target->position().pos();
|
||||
const auto MON_FROM_OLD = g_pCompositor->getMonitorFromVector(OLD_POS);
|
||||
const auto NEW_POS = MON_FROM_OLD ? OLD_POS - MON_FROM_OLD->m_position + THIS_MON_POS : OLD_POS;
|
||||
|
||||
// put around the current center, fit in workArea
|
||||
target->setPositionGlobal(fitBoxInWorkArea(CBox{NEW_POS, LAST_SIZE}, target));
|
||||
}
|
||||
}
|
||||
|
||||
CBox CDefaultFloatingAlgorithm::fitBoxInWorkArea(const CBox& box, SP<ITarget> t) {
|
||||
const auto WORK_AREA = m_parent->space()->workArea(true);
|
||||
const auto EXTENTS = t->window() ? t->window()->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS) : SBoxExtents{};
|
||||
CBox targetBox = box.copy().addExtents(EXTENTS);
|
||||
|
||||
targetBox.x = std::max(targetBox.x, WORK_AREA.x);
|
||||
targetBox.y = std::max(targetBox.y, WORK_AREA.y);
|
||||
|
||||
if (targetBox.x + targetBox.w > WORK_AREA.x + WORK_AREA.w)
|
||||
targetBox.x = WORK_AREA.x + WORK_AREA.w - targetBox.w;
|
||||
|
||||
if (targetBox.y + targetBox.h > WORK_AREA.y + WORK_AREA.h)
|
||||
targetBox.y = WORK_AREA.y + WORK_AREA.h - targetBox.h;
|
||||
|
||||
return targetBox.addExtents(SBoxExtents{.topLeft = -EXTENTS.topLeft, .bottomRight = -EXTENTS.bottomRight});
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::removeTarget(SP<ITarget> target) {
|
||||
target->rememberFloatingSize(target->position().size());
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
auto pos = target->position();
|
||||
pos.w += Δ.x;
|
||||
pos.h += Δ.y;
|
||||
pos.translate(-Δ / 2.F);
|
||||
target->setPositionGlobal(pos);
|
||||
|
||||
if (g_layoutManager->dragController()->target() == target)
|
||||
target->warpPositionSize();
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
||||
auto pos = target->position();
|
||||
pos.translate(Δ);
|
||||
target->setPositionGlobal(pos);
|
||||
|
||||
if (g_layoutManager->dragController()->target() == target)
|
||||
target->warpPositionSize();
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||
auto posABackup = a->position();
|
||||
a->setPositionGlobal(b->position());
|
||||
b->setPositionGlobal(posABackup);
|
||||
}
|
||||
|
||||
void CDefaultFloatingAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
auto pos = t->position();
|
||||
auto work = m_parent->space()->workArea(true);
|
||||
|
||||
const auto EXTENTS = t->window() ? t->window()->getWindowExtentsUnified(Desktop::View::RESERVED_EXTENTS) : SBoxExtents{};
|
||||
|
||||
switch (dir) {
|
||||
case Math::DIRECTION_LEFT: pos.x = work.x + EXTENTS.topLeft.x; break;
|
||||
case Math::DIRECTION_RIGHT: pos.x = work.x + work.w - pos.w - EXTENTS.bottomRight.x; break;
|
||||
case Math::DIRECTION_UP: pos.y = work.y + EXTENTS.topLeft.y; break;
|
||||
case Math::DIRECTION_DOWN: pos.y = work.y + work.h - pos.h - EXTENTS.bottomRight.y; break;
|
||||
default: Log::logger->log(Log::ERR, "Invalid direction in CDefaultFloatingAlgorithm::moveTargetInDirection"); break;
|
||||
}
|
||||
|
||||
t->setPositionGlobal(pos);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#include "../../FloatingAlgorithm.hpp"
|
||||
|
||||
namespace Layout {
|
||||
class CAlgorithm;
|
||||
}
|
||||
|
||||
namespace Layout::Floating {
|
||||
class CDefaultFloatingAlgorithm : public IFloatingAlgorithm {
|
||||
public:
|
||||
CDefaultFloatingAlgorithm() = default;
|
||||
virtual ~CDefaultFloatingAlgorithm() = default;
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
private:
|
||||
CBox fitBoxInWorkArea(const CBox& box, SP<ITarget> t);
|
||||
};
|
||||
};
|
||||
772
src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp
Normal file
772
src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.cpp
Normal file
|
|
@ -0,0 +1,772 @@
|
|||
#include "DwindleAlgorithm.hpp"
|
||||
|
||||
#include "../../Algorithm.hpp"
|
||||
#include "../../../space/Space.hpp"
|
||||
#include "../../../target/WindowTarget.hpp"
|
||||
#include "../../../LayoutManager.hpp"
|
||||
|
||||
#include "../../../../config/ConfigValue.hpp"
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../helpers/Monitor.hpp"
|
||||
#include "../../../../Compositor.hpp"
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Layout::Tiled;
|
||||
|
||||
struct Layout::Tiled::SDwindleNodeData {
|
||||
WP<SDwindleNodeData> pParent;
|
||||
bool isNode = false;
|
||||
WP<ITarget> pTarget;
|
||||
std::array<WP<SDwindleNodeData>, 2> children = {};
|
||||
WP<SDwindleNodeData> self;
|
||||
bool splitTop = false; // for preserve_split
|
||||
CBox box = {0};
|
||||
float splitRatio = 1.f;
|
||||
bool valid = true;
|
||||
bool ignoreFullscreenChecks = false;
|
||||
|
||||
// For list lookup
|
||||
bool operator==(const SDwindleNodeData& rhs) const {
|
||||
return pTarget.lock() == rhs.pTarget.lock() && box == rhs.box && pParent == rhs.pParent && children[0] == rhs.children[0] && children[1] == rhs.children[1];
|
||||
}
|
||||
|
||||
void recalcSizePosRecursive(bool force = false, bool horizontalOverride = false, bool verticalOverride = false) {
|
||||
if (children[0]) {
|
||||
static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split");
|
||||
static auto PPRESERVESPLIT = CConfigValue<Hyprlang::INT>("dwindle:preserve_split");
|
||||
static auto PFLMULT = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
|
||||
|
||||
if (*PPRESERVESPLIT == 0 && *PSMARTSPLIT == 0)
|
||||
splitTop = box.h * *PFLMULT > box.w;
|
||||
|
||||
if (verticalOverride)
|
||||
splitTop = true;
|
||||
else if (horizontalOverride)
|
||||
splitTop = false;
|
||||
|
||||
const auto SPLITSIDE = !splitTop;
|
||||
|
||||
if (SPLITSIDE) {
|
||||
// split left/right
|
||||
const float FIRSTSIZE = box.w / 2.0 * splitRatio;
|
||||
children[0]->box = CBox{box.x, box.y, FIRSTSIZE, box.h}.noNegativeSize();
|
||||
children[1]->box = CBox{box.x + FIRSTSIZE, box.y, box.w - FIRSTSIZE, box.h}.noNegativeSize();
|
||||
} else {
|
||||
// split top/bottom
|
||||
const float FIRSTSIZE = box.h / 2.0 * splitRatio;
|
||||
children[0]->box = CBox{box.x, box.y, box.w, FIRSTSIZE}.noNegativeSize();
|
||||
children[1]->box = CBox{box.x, box.y + FIRSTSIZE, box.w, box.h - FIRSTSIZE}.noNegativeSize();
|
||||
}
|
||||
|
||||
children[0]->recalcSizePosRecursive(force);
|
||||
children[1]->recalcSizePosRecursive(force);
|
||||
} else
|
||||
pTarget->setPositionGlobal(box);
|
||||
}
|
||||
};
|
||||
|
||||
void CDwindleAlgorithm::newTarget(SP<ITarget> target) {
|
||||
addTarget(target);
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::addTarget(SP<ITarget> target, bool newTarget) {
|
||||
const auto WORK_AREA = m_parent->space()->workArea();
|
||||
|
||||
const auto PNODE = m_dwindleNodesData.emplace_back(makeShared<SDwindleNodeData>());
|
||||
PNODE->self = PNODE;
|
||||
|
||||
const auto PMONITOR = m_parent->space()->workspace()->m_monitor;
|
||||
const auto PWORKSPACE = m_parent->space()->workspace();
|
||||
|
||||
static auto PUSEACTIVE = CConfigValue<Hyprlang::INT>("dwindle:use_active_for_splits");
|
||||
static auto PDEFAULTSPLIT = CConfigValue<Hyprlang::FLOAT>("dwindle:default_split_ratio");
|
||||
|
||||
// Populate the node with our window's data
|
||||
PNODE->pTarget = target;
|
||||
PNODE->isNode = false;
|
||||
|
||||
SP<SDwindleNodeData> OPENINGON;
|
||||
|
||||
const auto MOUSECOORDS = m_overrideFocalPoint.value_or(g_pInputManager->getMouseCoordsInternal());
|
||||
const auto ACTIVE_MON = Desktop::focusState()->monitor();
|
||||
|
||||
if ((PWORKSPACE == ACTIVE_MON->m_activeWorkspace || (PWORKSPACE->m_isSpecialWorkspace && PMONITOR->m_activeSpecialWorkspace)) && !*PUSEACTIVE) {
|
||||
OPENINGON = getNodeFromWindow(
|
||||
g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::SKIP_FULLSCREEN_PRIORITY));
|
||||
|
||||
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON))
|
||||
OPENINGON = getClosestNode(MOUSECOORDS);
|
||||
|
||||
} else if (*PUSEACTIVE) {
|
||||
if (Desktop::focusState()->window() && !Desktop::focusState()->window()->m_isFloating && Desktop::focusState()->window() != target->window() &&
|
||||
Desktop::focusState()->window()->m_workspace == PWORKSPACE && Desktop::focusState()->window()->m_isMapped) {
|
||||
OPENINGON = getNodeFromWindow(Desktop::focusState()->window());
|
||||
} else {
|
||||
OPENINGON = getNodeFromWindow(g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS));
|
||||
}
|
||||
|
||||
if (!OPENINGON && g_pCompositor->isPointOnReservedArea(MOUSECOORDS, ACTIVE_MON))
|
||||
OPENINGON = getClosestNode(MOUSECOORDS);
|
||||
|
||||
} else
|
||||
OPENINGON = getFirstNode();
|
||||
|
||||
// first, check if OPENINGON isn't too big.
|
||||
const auto PREDSIZEMAX = OPENINGON ? Vector2D(OPENINGON->box.w, OPENINGON->box.h) : PMONITOR->m_size;
|
||||
if (const auto MAXSIZE = target->maxSize().value_or(Math::VECTOR2D_MAX); MAXSIZE.x < PREDSIZEMAX.x || MAXSIZE.y < PREDSIZEMAX.y) {
|
||||
// we can't continue. make it floating.
|
||||
std::erase(m_dwindleNodesData, PNODE);
|
||||
m_parent->setFloating(target, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// last fail-safe to avoid duplicate fullscreens
|
||||
if ((!OPENINGON || OPENINGON->pTarget.lock() == target) && getNodes() > 1) {
|
||||
for (auto& node : m_dwindleNodesData) {
|
||||
if (node->pTarget.lock() && node->pTarget.lock() != target) {
|
||||
OPENINGON = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's the first, it's easy. Make it fullscreen.
|
||||
if (!OPENINGON || OPENINGON->pTarget.lock() == target) {
|
||||
PNODE->box = WORK_AREA;
|
||||
PNODE->pTarget->setPositionGlobal(PNODE->box);
|
||||
return;
|
||||
}
|
||||
|
||||
// get the node under our cursor
|
||||
|
||||
const auto NEWPARENT = m_dwindleNodesData.emplace_back(makeShared<SDwindleNodeData>());
|
||||
|
||||
// make the parent have the OPENINGON's stats
|
||||
NEWPARENT->box = OPENINGON->box;
|
||||
NEWPARENT->pParent = OPENINGON->pParent;
|
||||
NEWPARENT->isNode = true; // it is a node
|
||||
NEWPARENT->splitRatio = std::clamp(*PDEFAULTSPLIT, 0.1F, 1.9F);
|
||||
|
||||
static auto PWIDTHMULTIPLIER = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
|
||||
|
||||
// if cursor over first child, make it first, etc
|
||||
const auto SIDEBYSIDE = NEWPARENT->box.w > NEWPARENT->box.h * *PWIDTHMULTIPLIER;
|
||||
NEWPARENT->splitTop = !SIDEBYSIDE;
|
||||
|
||||
static auto PFORCESPLIT = CConfigValue<Hyprlang::INT>("dwindle:force_split");
|
||||
static auto PERMANENTDIRECTIONOVERRIDE = CConfigValue<Hyprlang::INT>("dwindle:permanent_direction_override");
|
||||
static auto PSMARTSPLIT = CConfigValue<Hyprlang::INT>("dwindle:smart_split");
|
||||
static auto PSPLITBIAS = CConfigValue<Hyprlang::INT>("dwindle:split_bias");
|
||||
|
||||
bool horizontalOverride = false;
|
||||
bool verticalOverride = false;
|
||||
|
||||
// let user select position -> top, right, bottom, left
|
||||
if (m_overrideDirection != Math::DIRECTION_DEFAULT) {
|
||||
|
||||
// this is horizontal
|
||||
if (m_overrideDirection % 2 == 0)
|
||||
verticalOverride = true;
|
||||
else
|
||||
horizontalOverride = true;
|
||||
|
||||
// 0 -> top and left | 1,2 -> right and bottom
|
||||
if (m_overrideDirection % 3 == 0) {
|
||||
NEWPARENT->children[1] = OPENINGON;
|
||||
NEWPARENT->children[0] = PNODE;
|
||||
} else {
|
||||
NEWPARENT->children[0] = OPENINGON;
|
||||
NEWPARENT->children[1] = PNODE;
|
||||
}
|
||||
|
||||
// whether or not the override persists after opening one window
|
||||
if (*PERMANENTDIRECTIONOVERRIDE == 0)
|
||||
m_overrideDirection = Math::DIRECTION_DEFAULT;
|
||||
} else if (*PSMARTSPLIT == 1) {
|
||||
const auto PARENT_CENTER = NEWPARENT->box.pos() + NEWPARENT->box.size() / 2;
|
||||
const auto PARENT_PROPORTIONS = NEWPARENT->box.h / NEWPARENT->box.w;
|
||||
const auto DELTA = MOUSECOORDS - PARENT_CENTER;
|
||||
const auto DELTA_SLOPE = DELTA.y / DELTA.x;
|
||||
|
||||
if (abs(DELTA_SLOPE) < PARENT_PROPORTIONS) {
|
||||
if (DELTA.x > 0) {
|
||||
// right
|
||||
NEWPARENT->splitTop = false;
|
||||
NEWPARENT->children[0] = OPENINGON;
|
||||
NEWPARENT->children[1] = PNODE;
|
||||
} else {
|
||||
// left
|
||||
NEWPARENT->splitTop = false;
|
||||
NEWPARENT->children[0] = PNODE;
|
||||
NEWPARENT->children[1] = OPENINGON;
|
||||
}
|
||||
} else {
|
||||
if (DELTA.y > 0) {
|
||||
// bottom
|
||||
NEWPARENT->splitTop = true;
|
||||
NEWPARENT->children[0] = OPENINGON;
|
||||
NEWPARENT->children[1] = PNODE;
|
||||
} else {
|
||||
// top
|
||||
NEWPARENT->splitTop = true;
|
||||
NEWPARENT->children[0] = PNODE;
|
||||
NEWPARENT->children[1] = OPENINGON;
|
||||
}
|
||||
}
|
||||
} else if (*PFORCESPLIT == 0 || !newTarget) {
|
||||
if ((SIDEBYSIDE &&
|
||||
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y + NEWPARENT->box.h)) ||
|
||||
(!SIDEBYSIDE &&
|
||||
VECINRECT(MOUSECOORDS, NEWPARENT->box.x, NEWPARENT->box.y / *PWIDTHMULTIPLIER, NEWPARENT->box.x + NEWPARENT->box.w, NEWPARENT->box.y + NEWPARENT->box.h / 2.f))) {
|
||||
// we are hovering over the first node, make PNODE first.
|
||||
NEWPARENT->children[1] = OPENINGON;
|
||||
NEWPARENT->children[0] = PNODE;
|
||||
} else {
|
||||
// we are hovering over the second node, make PNODE second.
|
||||
NEWPARENT->children[0] = OPENINGON;
|
||||
NEWPARENT->children[1] = PNODE;
|
||||
}
|
||||
} else {
|
||||
if (*PFORCESPLIT == 1) {
|
||||
NEWPARENT->children[1] = OPENINGON;
|
||||
NEWPARENT->children[0] = PNODE;
|
||||
} else {
|
||||
NEWPARENT->children[0] = OPENINGON;
|
||||
NEWPARENT->children[1] = PNODE;
|
||||
}
|
||||
}
|
||||
|
||||
// split in favor of a specific window
|
||||
if (*PSPLITBIAS && NEWPARENT->children[0] == PNODE)
|
||||
NEWPARENT->splitRatio = 2.f - NEWPARENT->splitRatio;
|
||||
|
||||
// and update the previous parent if it exists
|
||||
if (OPENINGON->pParent) {
|
||||
if (OPENINGON->pParent->children[0] == OPENINGON) {
|
||||
OPENINGON->pParent->children[0] = NEWPARENT;
|
||||
} else {
|
||||
OPENINGON->pParent->children[1] = NEWPARENT;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the children
|
||||
if (!verticalOverride && (NEWPARENT->box.w * *PWIDTHMULTIPLIER > NEWPARENT->box.h || horizontalOverride)) {
|
||||
// split left/right -> forced
|
||||
OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)};
|
||||
PNODE->box = {Vector2D(NEWPARENT->box.x + NEWPARENT->box.w / 2.f, NEWPARENT->box.y), Vector2D(NEWPARENT->box.w / 2.f, NEWPARENT->box.h)};
|
||||
} else {
|
||||
// split top/bottom
|
||||
OPENINGON->box = {NEWPARENT->box.pos(), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)};
|
||||
PNODE->box = {Vector2D(NEWPARENT->box.x, NEWPARENT->box.y + NEWPARENT->box.h / 2.f), Vector2D(NEWPARENT->box.w, NEWPARENT->box.h / 2.f)};
|
||||
}
|
||||
|
||||
OPENINGON->pParent = NEWPARENT;
|
||||
PNODE->pParent = NEWPARENT;
|
||||
|
||||
NEWPARENT->recalcSizePosRecursive(false, horizontalOverride, verticalOverride);
|
||||
|
||||
calculateWorkspace();
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint) {
|
||||
m_overrideFocalPoint = focalPoint;
|
||||
addTarget(target, false);
|
||||
m_overrideFocalPoint.reset();
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::removeTarget(SP<ITarget> target) {
|
||||
const auto PNODE = getNodeFromTarget(target);
|
||||
|
||||
if (!PNODE) {
|
||||
Log::logger->log(Log::ERR, "onWindowRemovedTiling node null?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target->fullscreenMode() != FSMODE_NONE)
|
||||
g_pCompositor->setWindowFullscreenInternal(target->window(), FSMODE_NONE);
|
||||
|
||||
const auto PPARENT = PNODE->pParent;
|
||||
|
||||
if (!PPARENT) {
|
||||
Log::logger->log(Log::DEBUG, "Removing last node (dwindle)");
|
||||
std::erase(m_dwindleNodesData, PNODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PSIBLING = PPARENT->children[0] == PNODE ? PPARENT->children[1] : PPARENT->children[0];
|
||||
|
||||
PSIBLING->pParent = PPARENT->pParent;
|
||||
|
||||
if (PPARENT->pParent != nullptr) {
|
||||
if (PPARENT->pParent->children[0] == PPARENT)
|
||||
PPARENT->pParent->children[0] = PSIBLING;
|
||||
else
|
||||
PPARENT->pParent->children[1] = PSIBLING;
|
||||
}
|
||||
|
||||
PPARENT->valid = false;
|
||||
PNODE->valid = false;
|
||||
|
||||
std::erase(m_dwindleNodesData, PPARENT);
|
||||
std::erase(m_dwindleNodesData, PNODE);
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
if (!validMapped(target->window()))
|
||||
return;
|
||||
|
||||
const auto PNODE = getNodeFromTarget(target);
|
||||
|
||||
if (!PNODE)
|
||||
return;
|
||||
|
||||
static auto PANIMATE = CConfigValue<Hyprlang::INT>("misc:animate_manual_resizes");
|
||||
static auto PSMARTRESIZING = CConfigValue<Hyprlang::INT>("dwindle:smart_resizing");
|
||||
|
||||
// get some data about our window
|
||||
const auto PMONITOR = m_parent->space()->workspace()->m_monitor;
|
||||
const auto MONITOR_WORKAREA = PMONITOR->logicalBoxMinusReserved();
|
||||
const auto BOX = target->position();
|
||||
const bool DISPLAYLEFT = STICKS(BOX.x, MONITOR_WORKAREA.x);
|
||||
const bool DISPLAYRIGHT = STICKS(BOX.x + BOX.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w);
|
||||
const bool DISPLAYTOP = STICKS(BOX.y, MONITOR_WORKAREA.y);
|
||||
const bool DISPLAYBOTTOM = STICKS(BOX.y + BOX.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h);
|
||||
|
||||
// construct allowed movement
|
||||
Vector2D allowedMovement = Δ;
|
||||
if (DISPLAYLEFT && DISPLAYRIGHT)
|
||||
allowedMovement.x = 0;
|
||||
|
||||
if (DISPLAYBOTTOM && DISPLAYTOP)
|
||||
allowedMovement.y = 0;
|
||||
|
||||
if (*PSMARTRESIZING == 1) {
|
||||
// Identify inner and outer nodes for both directions
|
||||
SP<SDwindleNodeData> PVOUTER = nullptr;
|
||||
SP<SDwindleNodeData> PVINNER = nullptr;
|
||||
SP<SDwindleNodeData> PHOUTER = nullptr;
|
||||
SP<SDwindleNodeData> PHINNER = nullptr;
|
||||
|
||||
const auto LEFT = corner == CORNER_TOPLEFT || corner == CORNER_BOTTOMLEFT || DISPLAYRIGHT;
|
||||
const auto TOP = corner == CORNER_TOPLEFT || corner == CORNER_TOPRIGHT || DISPLAYBOTTOM;
|
||||
const auto RIGHT = corner == CORNER_TOPRIGHT || corner == CORNER_BOTTOMRIGHT || DISPLAYLEFT;
|
||||
const auto BOTTOM = corner == CORNER_BOTTOMLEFT || corner == CORNER_BOTTOMRIGHT || DISPLAYTOP;
|
||||
const auto NONE = corner == CORNER_NONE;
|
||||
|
||||
for (auto PCURRENT = PNODE; PCURRENT && PCURRENT->pParent; PCURRENT = PCURRENT->pParent.lock()) {
|
||||
const auto PPARENT = PCURRENT->pParent;
|
||||
|
||||
if (!PVOUTER && PPARENT->splitTop && (NONE || (TOP && PPARENT->children[1] == PCURRENT) || (BOTTOM && PPARENT->children[0] == PCURRENT)))
|
||||
PVOUTER = PCURRENT;
|
||||
else if (!PVOUTER && !PVINNER && PPARENT->splitTop)
|
||||
PVINNER = PCURRENT;
|
||||
else if (!PHOUTER && !PPARENT->splitTop && (NONE || (LEFT && PPARENT->children[1] == PCURRENT) || (RIGHT && PPARENT->children[0] == PCURRENT)))
|
||||
PHOUTER = PCURRENT;
|
||||
else if (!PHOUTER && !PHINNER && !PPARENT->splitTop)
|
||||
PHINNER = PCURRENT;
|
||||
|
||||
if (PVOUTER && PHOUTER)
|
||||
break;
|
||||
}
|
||||
|
||||
if (PHOUTER) {
|
||||
PHOUTER->pParent->splitRatio = std::clamp(PHOUTER->pParent->splitRatio + allowedMovement.x * 2.f / PHOUTER->pParent->box.w, 0.1, 1.9);
|
||||
|
||||
if (PHINNER) {
|
||||
const auto ORIGINAL = PHINNER->box.w;
|
||||
PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
if (PHINNER->pParent->children[0] == PHINNER)
|
||||
PHINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9);
|
||||
else
|
||||
PHINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.x) / PHINNER->pParent->box.w * 2.f, 0.1, 1.9);
|
||||
PHINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
} else
|
||||
PHOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
}
|
||||
|
||||
if (PVOUTER) {
|
||||
PVOUTER->pParent->splitRatio = std::clamp(PVOUTER->pParent->splitRatio + allowedMovement.y * 2.f / PVOUTER->pParent->box.h, 0.1, 1.9);
|
||||
|
||||
if (PVINNER) {
|
||||
const auto ORIGINAL = PVINNER->box.h;
|
||||
PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
if (PVINNER->pParent->children[0] == PVINNER)
|
||||
PVINNER->pParent->splitRatio = std::clamp((ORIGINAL - allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9);
|
||||
else
|
||||
PVINNER->pParent->splitRatio = std::clamp(2 - (ORIGINAL + allowedMovement.y) / PVINNER->pParent->box.h * 2.f, 0.1, 1.9);
|
||||
PVINNER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
} else
|
||||
PVOUTER->pParent->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
}
|
||||
} else {
|
||||
// get the correct containers to apply splitratio to
|
||||
const auto PPARENT = PNODE->pParent;
|
||||
|
||||
if (!PPARENT)
|
||||
return; // the only window on a workspace, ignore
|
||||
|
||||
const bool PARENTSIDEBYSIDE = !PPARENT->splitTop;
|
||||
|
||||
// Get the parent's parent
|
||||
auto PPARENT2 = PPARENT->pParent;
|
||||
|
||||
Hyprutils::Utils::CScopeGuard x([target, this] {
|
||||
// snap all windows, don't animate resizes if they are manual
|
||||
if (target == g_layoutManager->dragController()->target()) {
|
||||
for (const auto& w : m_dwindleNodesData) {
|
||||
if (w->isNode)
|
||||
continue;
|
||||
|
||||
w->pTarget->warpPositionSize();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// No parent means we have only 2 windows, and thus one axis of freedom
|
||||
if (!PPARENT2) {
|
||||
if (PARENTSIDEBYSIDE) {
|
||||
allowedMovement.x *= 2.f / PPARENT->box.w;
|
||||
PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9);
|
||||
PPARENT->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
} else {
|
||||
allowedMovement.y *= 2.f / PPARENT->box.h;
|
||||
PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9);
|
||||
PPARENT->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get first parent with other split
|
||||
while (PPARENT2 && PPARENT2->splitTop == !PARENTSIDEBYSIDE)
|
||||
PPARENT2 = PPARENT2->pParent;
|
||||
|
||||
// no parent, one axis of freedom
|
||||
if (!PPARENT2) {
|
||||
if (PARENTSIDEBYSIDE) {
|
||||
allowedMovement.x *= 2.f / PPARENT->box.w;
|
||||
PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.x, 0.1, 1.9);
|
||||
PPARENT->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
} else {
|
||||
allowedMovement.y *= 2.f / PPARENT->box.h;
|
||||
PPARENT->splitRatio = std::clamp(PPARENT->splitRatio + allowedMovement.y, 0.1, 1.9);
|
||||
PPARENT->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 2 axes of freedom
|
||||
const auto SIDECONTAINER = PARENTSIDEBYSIDE ? PPARENT : PPARENT2;
|
||||
const auto TOPCONTAINER = PARENTSIDEBYSIDE ? PPARENT2 : PPARENT;
|
||||
|
||||
allowedMovement.x *= 2.f / SIDECONTAINER->box.w;
|
||||
allowedMovement.y *= 2.f / TOPCONTAINER->box.h;
|
||||
|
||||
SIDECONTAINER->splitRatio = std::clamp(SIDECONTAINER->splitRatio + allowedMovement.x, 0.1, 1.9);
|
||||
TOPCONTAINER->splitRatio = std::clamp(TOPCONTAINER->splitRatio + allowedMovement.y, 0.1, 1.9);
|
||||
SIDECONTAINER->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
TOPCONTAINER->recalcSizePosRecursive(*PANIMATE == 0);
|
||||
}
|
||||
|
||||
// snap all windows, don't animate resizes if they are manual
|
||||
if (target == g_layoutManager->dragController()->target()) {
|
||||
for (const auto& w : m_dwindleNodesData) {
|
||||
if (w->isNode)
|
||||
continue;
|
||||
|
||||
w->pTarget->warpPositionSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SP<ITarget> CDwindleAlgorithm::getNextCandidate(SP<ITarget> old) {
|
||||
const auto MIDDLE = old->position().middle();
|
||||
|
||||
if (const auto NODE = getClosestNode(MIDDLE); NODE)
|
||||
return NODE->pTarget.lock();
|
||||
|
||||
if (const auto NODE = getFirstNode(); NODE)
|
||||
return NODE->pTarget.lock();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||
auto nodeA = getNodeFromTarget(a);
|
||||
auto nodeB = getNodeFromTarget(b);
|
||||
|
||||
if (nodeA)
|
||||
nodeA->pTarget = b;
|
||||
if (nodeB)
|
||||
nodeB->pTarget = a;
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::recalculate() {
|
||||
calculateWorkspace();
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CDwindleAlgorithm::predictSizeForNewTarget() {
|
||||
// get window candidate
|
||||
PHLWINDOW candidate = Desktop::focusState()->window();
|
||||
|
||||
if (!candidate || candidate->m_workspace != m_parent->space()->workspace())
|
||||
candidate = m_parent->space()->workspace()->getFirstWindow();
|
||||
|
||||
// create a fake node
|
||||
SDwindleNodeData node;
|
||||
|
||||
if (!candidate)
|
||||
return Desktop::focusState()->monitor()->m_size;
|
||||
else {
|
||||
const auto PNODE = getNodeFromWindow(candidate);
|
||||
|
||||
if (!PNODE)
|
||||
return {};
|
||||
|
||||
node = *PNODE;
|
||||
node.pTarget.reset();
|
||||
|
||||
CBox box = PNODE->box;
|
||||
|
||||
static auto PFLMULT = CConfigValue<Hyprlang::FLOAT>("dwindle:split_width_multiplier");
|
||||
|
||||
bool splitTop = box.h * *PFLMULT > box.w;
|
||||
|
||||
const auto SPLITSIDE = !splitTop;
|
||||
|
||||
if (SPLITSIDE)
|
||||
node.box = {{}, {box.w / 2.0, box.h}};
|
||||
else
|
||||
node.box = {{}, {box.w, box.h / 2.0}};
|
||||
|
||||
// TODO: make this better and more accurate
|
||||
|
||||
return node.box.size();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
const auto PNODE = getNodeFromTarget(t);
|
||||
const Vector2D originalPos = t->position().middle();
|
||||
|
||||
if (!PNODE || !t->window())
|
||||
return;
|
||||
|
||||
Vector2D focalPoint;
|
||||
|
||||
const auto WINDOWIDEALBB =
|
||||
t->fullscreenMode() != FSMODE_NONE ? m_parent->space()->workspace()->m_monitor->logicalBox() : t->window()->getWindowIdealBoundingBoxIgnoreReserved();
|
||||
|
||||
switch (dir) {
|
||||
case Math::DIRECTION_UP: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, -1.0}; break;
|
||||
case Math::DIRECTION_DOWN: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x / 2.0, WINDOWIDEALBB.size().y + 1.0}; break;
|
||||
case Math::DIRECTION_LEFT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{-1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
||||
case Math::DIRECTION_RIGHT: focalPoint = WINDOWIDEALBB.pos() + Vector2D{WINDOWIDEALBB.size().x + 1.0, WINDOWIDEALBB.size().y / 2.0}; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
t->window()->setAnimationsToMove();
|
||||
|
||||
removeTarget(t);
|
||||
|
||||
const auto PMONITORFOCAL = g_pCompositor->getMonitorFromVector(focalPoint);
|
||||
|
||||
if (PMONITORFOCAL != m_parent->space()->workspace()->m_monitor) {
|
||||
// move with a focal point
|
||||
|
||||
if (PMONITORFOCAL->m_activeWorkspace)
|
||||
t->assignToSpace(PMONITORFOCAL->m_activeWorkspace->m_space);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
movedTarget(t, focalPoint);
|
||||
|
||||
// restore focus to the previous position
|
||||
if (silent) {
|
||||
const auto PNODETOFOCUS = getClosestNode(originalPos);
|
||||
if (PNODETOFOCUS && PNODETOFOCUS->pTarget)
|
||||
Desktop::focusState()->fullWindowFocus(PNODETOFOCUS->pTarget->window(), Desktop::FOCUS_REASON_KEYBIND);
|
||||
}
|
||||
}
|
||||
|
||||
// --------- internal --------- //
|
||||
|
||||
void CDwindleAlgorithm::calculateWorkspace() {
|
||||
const auto PWORKSPACE = m_parent->space()->workspace();
|
||||
const auto PMONITOR = PWORKSPACE->m_monitor;
|
||||
|
||||
if (!PMONITOR || PWORKSPACE->m_hasFullscreenWindow)
|
||||
return;
|
||||
|
||||
const auto TOPNODE = getMasterNode();
|
||||
|
||||
if (TOPNODE) {
|
||||
TOPNODE->box = m_parent->space()->workArea();
|
||||
TOPNODE->recalcSizePosRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
SP<SDwindleNodeData> CDwindleAlgorithm::getNodeFromTarget(SP<ITarget> t) {
|
||||
for (const auto& n : m_dwindleNodesData) {
|
||||
if (n->pTarget == t)
|
||||
return n;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<SDwindleNodeData> CDwindleAlgorithm::getNodeFromWindow(PHLWINDOW w) {
|
||||
return w ? getNodeFromTarget(w->layoutTarget()) : nullptr;
|
||||
}
|
||||
|
||||
int CDwindleAlgorithm::getNodes() {
|
||||
return m_dwindleNodesData.size();
|
||||
}
|
||||
|
||||
SP<SDwindleNodeData> CDwindleAlgorithm::getFirstNode() {
|
||||
return m_dwindleNodesData.empty() ? nullptr : m_dwindleNodesData.at(0);
|
||||
}
|
||||
|
||||
SP<SDwindleNodeData> CDwindleAlgorithm::getClosestNode(const Vector2D& point) {
|
||||
SP<SDwindleNodeData> res = nullptr;
|
||||
double distClosest = -1;
|
||||
for (auto& n : m_dwindleNodesData) {
|
||||
if (n->pTarget && Desktop::View::validMapped(n->pTarget->window())) {
|
||||
auto distAnother = vecToRectDistanceSquared(point, n->box.pos(), n->box.pos() + n->box.size());
|
||||
if (!res || distAnother < distClosest) {
|
||||
res = n;
|
||||
distClosest = distAnother;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
SP<SDwindleNodeData> CDwindleAlgorithm::getMasterNode() {
|
||||
for (auto& n : m_dwindleNodesData) {
|
||||
if (!n->pParent)
|
||||
return n;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::expected<void, std::string> CDwindleAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||
const auto ARGS = CVarList2(std::string{sv}, 0, ' ');
|
||||
|
||||
const auto CURRENT_NODE = getNodeFromWindow(Desktop::focusState()->window());
|
||||
|
||||
if (ARGS[0] == "togglesplit") {
|
||||
if (CURRENT_NODE)
|
||||
toggleSplit(CURRENT_NODE);
|
||||
} else if (ARGS[0] == "swapsplit") {
|
||||
if (CURRENT_NODE)
|
||||
swapSplit(CURRENT_NODE);
|
||||
} else if (ARGS[0] == "movetoroot") {
|
||||
auto node = CURRENT_NODE;
|
||||
if (!ARGS[1].empty()) {
|
||||
auto w = g_pCompositor->getWindowByRegex(std::string{ARGS[1]});
|
||||
if (w)
|
||||
node = getNodeFromWindow(w);
|
||||
}
|
||||
|
||||
const auto STABLE = ARGS[2].empty() || ARGS[2] != "unstable";
|
||||
moveToRoot(node, STABLE);
|
||||
} else if (ARGS[0] == "preselect") {
|
||||
auto direction = ARGS[1];
|
||||
|
||||
if (direction.empty()) {
|
||||
Log::logger->log(Log::ERR, "Expected direction for preselect");
|
||||
return std::unexpected("No direction for preselect");
|
||||
}
|
||||
|
||||
switch (direction.front()) {
|
||||
case 'u':
|
||||
case 't': {
|
||||
m_overrideDirection = Math::DIRECTION_UP;
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'b': {
|
||||
m_overrideDirection = Math::DIRECTION_DOWN;
|
||||
break;
|
||||
}
|
||||
case 'r': {
|
||||
m_overrideDirection = Math::DIRECTION_RIGHT;
|
||||
break;
|
||||
}
|
||||
case 'l': {
|
||||
m_overrideDirection = Math::DIRECTION_LEFT;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// any other character resets the focus direction
|
||||
// needed for the persistent mode
|
||||
m_overrideDirection = Math::DIRECTION_DEFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::toggleSplit(SP<SDwindleNodeData> x) {
|
||||
if (!x || !x->pParent)
|
||||
return;
|
||||
|
||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||
return;
|
||||
|
||||
x->pParent->splitTop = !x->pParent->splitTop;
|
||||
|
||||
x->pParent->recalcSizePosRecursive();
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::swapSplit(SP<SDwindleNodeData> x) {
|
||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||
return;
|
||||
|
||||
std::swap(x->pParent->children[0], x->pParent->children[1]);
|
||||
|
||||
x->pParent->recalcSizePosRecursive();
|
||||
}
|
||||
|
||||
void CDwindleAlgorithm::moveToRoot(SP<SDwindleNodeData> x, bool stable) {
|
||||
if (!x || !x->pParent)
|
||||
return;
|
||||
|
||||
if (x->pTarget->fullscreenMode() != FSMODE_NONE)
|
||||
return;
|
||||
|
||||
// already at root
|
||||
if (!x->pParent->pParent)
|
||||
return;
|
||||
|
||||
auto& pNode = x->pParent->children[0] == x ? x->pParent->children[0] : x->pParent->children[1];
|
||||
|
||||
// instead of [getMasterNodeOnWorkspace], we walk back to root since we need
|
||||
// to know which children of root is our ancestor
|
||||
auto pAncestor = x, pRoot = x->pParent.lock();
|
||||
while (pRoot->pParent) {
|
||||
pAncestor = pRoot;
|
||||
pRoot = pRoot->pParent.lock();
|
||||
}
|
||||
|
||||
auto& pSwap = pRoot->children[0] == pAncestor ? pRoot->children[1] : pRoot->children[0];
|
||||
std::swap(pNode, pSwap);
|
||||
std::swap(pNode->pParent, pSwap->pParent);
|
||||
|
||||
// [stable] in that the focused window occupies same side of screen
|
||||
if (stable)
|
||||
std::swap(pRoot->children[0], pRoot->children[1]);
|
||||
|
||||
pRoot->recalcSizePosRecursive();
|
||||
}
|
||||
57
src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp
Normal file
57
src/layout/algorithm/tiled/dwindle/DwindleAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#include "../../TiledAlgorithm.hpp"
|
||||
|
||||
namespace Layout {
|
||||
class CAlgorithm;
|
||||
}
|
||||
|
||||
namespace Layout::Tiled {
|
||||
struct SDwindleNodeData;
|
||||
|
||||
class CDwindleAlgorithm : public ITiledAlgorithm {
|
||||
public:
|
||||
CDwindleAlgorithm() = default;
|
||||
virtual ~CDwindleAlgorithm() = default;
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
virtual std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
private:
|
||||
std::vector<SP<SDwindleNodeData>> m_dwindleNodesData;
|
||||
|
||||
struct {
|
||||
bool started = false;
|
||||
bool pseudo = false;
|
||||
bool xExtent = false;
|
||||
bool yExtent = false;
|
||||
} m_pseudoDragFlags;
|
||||
|
||||
std::optional<Vector2D> m_overrideFocalPoint; // for onWindowCreatedTiling.
|
||||
|
||||
void addTarget(SP<ITarget> target, bool newTarget = true);
|
||||
void calculateWorkspace();
|
||||
SP<SDwindleNodeData> getNodeFromTarget(SP<ITarget>);
|
||||
SP<SDwindleNodeData> getNodeFromWindow(PHLWINDOW w);
|
||||
int getNodes();
|
||||
SP<SDwindleNodeData> getFirstNode();
|
||||
SP<SDwindleNodeData> getClosestNode(const Vector2D&);
|
||||
SP<SDwindleNodeData> getMasterNode();
|
||||
|
||||
void toggleSplit(SP<SDwindleNodeData>);
|
||||
void swapSplit(SP<SDwindleNodeData>);
|
||||
void moveToRoot(SP<SDwindleNodeData>, bool stable = true);
|
||||
|
||||
Math::eDirection m_overrideDirection = Math::DIRECTION_DEFAULT;
|
||||
};
|
||||
};
|
||||
1292
src/layout/algorithm/tiled/master/MasterAlgorithm.cpp
Normal file
1292
src/layout/algorithm/tiled/master/MasterAlgorithm.cpp
Normal file
File diff suppressed because it is too large
Load diff
75
src/layout/algorithm/tiled/master/MasterAlgorithm.hpp
Normal file
75
src/layout/algorithm/tiled/master/MasterAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "../../TiledAlgorithm.hpp"
|
||||
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
|
||||
namespace Layout {
|
||||
class CAlgorithm;
|
||||
}
|
||||
|
||||
namespace Layout::Tiled {
|
||||
struct SMasterNodeData;
|
||||
|
||||
//orientation determines which side of the screen the master area resides
|
||||
enum eOrientation : uint8_t {
|
||||
ORIENTATION_LEFT = 0,
|
||||
ORIENTATION_TOP,
|
||||
ORIENTATION_RIGHT,
|
||||
ORIENTATION_BOTTOM,
|
||||
ORIENTATION_CENTER
|
||||
};
|
||||
|
||||
struct SMasterWorkspaceData {
|
||||
WORKSPACEID workspaceID = WORKSPACE_INVALID;
|
||||
eOrientation orientation = ORIENTATION_LEFT;
|
||||
// Previously focused non-master window when `focusmaster previous` command was issued
|
||||
WP<ITarget> focusMasterPrev;
|
||||
|
||||
//
|
||||
bool operator==(const SMasterWorkspaceData& rhs) const {
|
||||
return workspaceID == rhs.workspaceID;
|
||||
}
|
||||
};
|
||||
|
||||
class CMasterAlgorithm : public ITiledAlgorithm {
|
||||
public:
|
||||
CMasterAlgorithm() = default;
|
||||
virtual ~CMasterAlgorithm() = default;
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
virtual std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
private:
|
||||
std::vector<SP<SMasterNodeData>> m_masterNodesData;
|
||||
SMasterWorkspaceData m_workspaceData;
|
||||
|
||||
void addTarget(SP<ITarget> target, bool firstMap);
|
||||
|
||||
bool m_forceWarps = false;
|
||||
|
||||
void buildOrientationCycleVectorFromVars(std::vector<eOrientation>& cycle, Hyprutils::String::CVarList2* vars);
|
||||
void buildOrientationCycleVectorFromEOperation(std::vector<eOrientation>& cycle);
|
||||
void runOrientationCycle(Hyprutils::String::CVarList2* vars, int next);
|
||||
eOrientation getDynamicOrientation();
|
||||
int getNodesNo();
|
||||
SP<SMasterNodeData> getNodeFromWindow(PHLWINDOW);
|
||||
SP<SMasterNodeData> getNodeFromTarget(SP<ITarget>);
|
||||
SP<SMasterNodeData> getMasterNode();
|
||||
SP<SMasterNodeData> getClosestNode(const Vector2D&);
|
||||
void calculateWorkspace();
|
||||
SP<ITarget> getNextTarget(SP<ITarget>, bool, bool);
|
||||
int getMastersNo();
|
||||
bool isWindowTiled(PHLWINDOW);
|
||||
};
|
||||
};
|
||||
274
src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp
Normal file
274
src/layout/algorithm/tiled/monocle/MonocleAlgorithm.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include "MonocleAlgorithm.hpp"
|
||||
|
||||
#include "../../Algorithm.hpp"
|
||||
#include "../../../space/Space.hpp"
|
||||
#include "../../../target/WindowTarget.hpp"
|
||||
#include "../../../LayoutManager.hpp"
|
||||
|
||||
#include "../../../../config/ConfigValue.hpp"
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../desktop/history/WindowHistoryTracker.hpp"
|
||||
#include "../../../../helpers/Monitor.hpp"
|
||||
#include "../../../../Compositor.hpp"
|
||||
|
||||
#include <hyprutils/string/VarList2.hpp>
|
||||
#include <hyprutils/string/ConstVarList.hpp>
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
||||
using namespace Hyprutils::String;
|
||||
using namespace Hyprutils::Utils;
|
||||
using namespace Layout;
|
||||
using namespace Layout::Tiled;
|
||||
|
||||
CMonocleAlgorithm::CMonocleAlgorithm() {
|
||||
// hook into focus changes to bring focused window to front
|
||||
m_focusCallback = g_pHookSystem->hookDynamic("activeWindow", [this](void* hk, SCallbackInfo& info, std::any param) {
|
||||
const auto PWINDOW = std::any_cast<Desktop::View::SWindowActiveEvent>(param).window;
|
||||
|
||||
if (!PWINDOW)
|
||||
return;
|
||||
|
||||
if (!PWINDOW->m_workspace->isVisible())
|
||||
return;
|
||||
|
||||
const auto TARGET = PWINDOW->layoutTarget();
|
||||
if (!TARGET)
|
||||
return;
|
||||
|
||||
focusTargetUpdate(TARGET);
|
||||
});
|
||||
}
|
||||
|
||||
CMonocleAlgorithm::~CMonocleAlgorithm() {
|
||||
// unhide all windows before destruction
|
||||
for (const auto& data : m_targetDatas) {
|
||||
const auto TARGET = data->target.lock();
|
||||
if (!TARGET)
|
||||
continue;
|
||||
|
||||
const auto WINDOW = TARGET->window();
|
||||
if (WINDOW)
|
||||
WINDOW->setHidden(false);
|
||||
}
|
||||
|
||||
m_focusCallback.reset();
|
||||
}
|
||||
|
||||
SP<SMonocleTargetData> CMonocleAlgorithm::dataFor(SP<ITarget> t) {
|
||||
for (auto& data : m_targetDatas) {
|
||||
if (data->target.lock() == t)
|
||||
return data;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::newTarget(SP<ITarget> target) {
|
||||
const auto DATA = m_targetDatas.emplace_back(makeShared<SMonocleTargetData>(target));
|
||||
|
||||
m_currentVisibleIndex = m_targetDatas.size() - 1;
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint) {
|
||||
newTarget(target);
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::removeTarget(SP<ITarget> target) {
|
||||
auto it = std::ranges::find_if(m_targetDatas, [target](const auto& data) { return data->target.lock() == target; });
|
||||
|
||||
if (it == m_targetDatas.end())
|
||||
return;
|
||||
|
||||
// unhide window when removing from monocle layout
|
||||
const auto WINDOW = target->window();
|
||||
if (WINDOW)
|
||||
WINDOW->setHidden(false);
|
||||
|
||||
const auto INDEX = std::distance(m_targetDatas.begin(), it);
|
||||
m_targetDatas.erase(it);
|
||||
|
||||
if (m_targetDatas.empty()) {
|
||||
m_currentVisibleIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// try to use the last window in history if we can
|
||||
for (const auto& historyWindow : Desktop::History::windowTracker()->historyForWorkspace(m_parent->space()->workspace()) | std::views::reverse) {
|
||||
auto it = std::ranges::find_if(m_targetDatas, [&historyWindow](const auto& d) { return d->target == historyWindow->layoutTarget(); });
|
||||
|
||||
if (it == m_targetDatas.end())
|
||||
continue;
|
||||
|
||||
// we found a historical target, use that first
|
||||
m_currentVisibleIndex = std::distance(m_targetDatas.begin(), it);
|
||||
|
||||
recalculate();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't find history, fall back to last
|
||||
|
||||
if (m_currentVisibleIndex >= (int)m_targetDatas.size())
|
||||
m_currentVisibleIndex = m_targetDatas.size() - 1;
|
||||
else if (INDEX <= m_currentVisibleIndex && m_currentVisibleIndex > 0)
|
||||
m_currentVisibleIndex--;
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
// monocle layout doesn't support manual resizing, all windows are fullscreen
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::recalculate() {
|
||||
if (m_targetDatas.empty())
|
||||
return;
|
||||
|
||||
const auto WORK_AREA = m_parent->space()->workArea();
|
||||
|
||||
for (size_t i = 0; i < m_targetDatas.size(); ++i) {
|
||||
const auto& DATA = m_targetDatas[i];
|
||||
const auto TARGET = DATA->target.lock();
|
||||
|
||||
if (!TARGET)
|
||||
continue;
|
||||
|
||||
const auto WINDOW = TARGET->window();
|
||||
if (!WINDOW)
|
||||
continue;
|
||||
|
||||
DATA->layoutBox = WORK_AREA;
|
||||
TARGET->setPositionGlobal(WORK_AREA);
|
||||
|
||||
const bool SHOULD_BE_VISIBLE = ((int)i == m_currentVisibleIndex);
|
||||
WINDOW->setHidden(!SHOULD_BE_VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
SP<ITarget> CMonocleAlgorithm::getNextCandidate(SP<ITarget> old) {
|
||||
if (m_targetDatas.empty())
|
||||
return nullptr;
|
||||
|
||||
auto it = std::ranges::find_if(m_targetDatas, [old](const auto& data) { return data->target.lock() == old; });
|
||||
|
||||
if (it == m_targetDatas.end()) {
|
||||
if (m_currentVisibleIndex >= 0 && m_currentVisibleIndex < (int)m_targetDatas.size())
|
||||
return m_targetDatas[m_currentVisibleIndex]->target.lock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto next = std::next(it);
|
||||
if (next == m_targetDatas.end())
|
||||
next = m_targetDatas.begin();
|
||||
|
||||
return next->get()->target.lock();
|
||||
}
|
||||
|
||||
std::expected<void, std::string> CMonocleAlgorithm::layoutMsg(const std::string_view& sv) {
|
||||
CVarList2 vars(std::string{sv}, 0, 's');
|
||||
|
||||
if (vars.size() < 1)
|
||||
return std::unexpected("layoutmsg requires at least 1 argument");
|
||||
|
||||
const auto COMMAND = vars[0];
|
||||
|
||||
if (COMMAND == "cyclenext") {
|
||||
cycleNext();
|
||||
return {};
|
||||
} else if (COMMAND == "cycleprev") {
|
||||
cyclePrev();
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::unexpected(std::format("Unknown monocle layoutmsg: {}", COMMAND));
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CMonocleAlgorithm::predictSizeForNewTarget() {
|
||||
const auto WORK_AREA = m_parent->space()->workArea();
|
||||
return WORK_AREA.size();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::swapTargets(SP<ITarget> a, SP<ITarget> b) {
|
||||
auto nodeA = dataFor(a);
|
||||
auto nodeB = dataFor(b);
|
||||
|
||||
if (nodeA)
|
||||
nodeA->target = b;
|
||||
if (nodeB)
|
||||
nodeB->target = a;
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
// try to find a monitor in the specified direction, thats the logical thing
|
||||
if (!t || !t->space() || !t->space()->workspace())
|
||||
return;
|
||||
|
||||
const auto PMONINDIR = g_pCompositor->getMonitorInDirection(t->space()->workspace()->m_monitor.lock(), dir);
|
||||
|
||||
// if we found a monitor, move the window there
|
||||
if (PMONINDIR && PMONINDIR != t->space()->workspace()->m_monitor.lock()) {
|
||||
const auto TARGETWS = PMONINDIR->m_activeWorkspace;
|
||||
|
||||
if (t->window())
|
||||
t->window()->setAnimationsToMove();
|
||||
|
||||
t->assignToSpace(TARGETWS->m_space);
|
||||
}
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::cycleNext() {
|
||||
if (m_targetDatas.empty())
|
||||
return;
|
||||
|
||||
m_currentVisibleIndex = (m_currentVisibleIndex + 1) % m_targetDatas.size();
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::cyclePrev() {
|
||||
if (m_targetDatas.empty())
|
||||
return;
|
||||
|
||||
m_currentVisibleIndex--;
|
||||
if (m_currentVisibleIndex < 0)
|
||||
m_currentVisibleIndex = m_targetDatas.size() - 1;
|
||||
updateVisible();
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::focusTargetUpdate(SP<ITarget> target) {
|
||||
auto it = std::ranges::find_if(m_targetDatas, [target](const auto& data) { return data->target.lock() == target; });
|
||||
|
||||
if (it == m_targetDatas.end())
|
||||
return;
|
||||
|
||||
const auto NEW_INDEX = std::distance(m_targetDatas.begin(), it);
|
||||
|
||||
if (m_currentVisibleIndex != NEW_INDEX) {
|
||||
m_currentVisibleIndex = NEW_INDEX;
|
||||
updateVisible();
|
||||
}
|
||||
}
|
||||
|
||||
void CMonocleAlgorithm::updateVisible() {
|
||||
recalculate();
|
||||
|
||||
const auto VISIBLE_TARGET = getVisibleTarget();
|
||||
if (!VISIBLE_TARGET)
|
||||
return;
|
||||
|
||||
const auto WINDOW = VISIBLE_TARGET->window();
|
||||
if (!WINDOW)
|
||||
return;
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(WINDOW, Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
}
|
||||
|
||||
SP<ITarget> CMonocleAlgorithm::getVisibleTarget() {
|
||||
if (m_currentVisibleIndex < 0 || m_currentVisibleIndex >= (int)m_targetDatas.size())
|
||||
return nullptr;
|
||||
|
||||
return m_targetDatas[m_currentVisibleIndex]->target.lock();
|
||||
}
|
||||
52
src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp
Normal file
52
src/layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../TiledAlgorithm.hpp"
|
||||
#include "../../../../managers/HookSystemManager.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Layout::Tiled {
|
||||
|
||||
struct SMonocleTargetData {
|
||||
SMonocleTargetData(SP<ITarget> t) : target(t) {
|
||||
;
|
||||
}
|
||||
|
||||
WP<ITarget> target;
|
||||
CBox layoutBox;
|
||||
};
|
||||
|
||||
class CMonocleAlgorithm : public ITiledAlgorithm {
|
||||
public:
|
||||
CMonocleAlgorithm();
|
||||
virtual ~CMonocleAlgorithm();
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
virtual std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
private:
|
||||
std::vector<SP<SMonocleTargetData>> m_targetDatas;
|
||||
SP<HOOK_CALLBACK_FN> m_focusCallback;
|
||||
|
||||
int m_currentVisibleIndex = 0;
|
||||
|
||||
SP<SMonocleTargetData> dataFor(SP<ITarget> t);
|
||||
void cycleNext();
|
||||
void cyclePrev();
|
||||
void focusTargetUpdate(SP<ITarget> target);
|
||||
void updateVisible();
|
||||
SP<ITarget> getVisibleTarget();
|
||||
};
|
||||
};
|
||||
293
src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
Normal file
293
src/layout/algorithm/tiled/scrolling/ScrollTapeController.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#include "ScrollTapeController.hpp"
|
||||
#include "ScrollingAlgorithm.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Layout::Tiled;
|
||||
|
||||
CScrollTapeController::CScrollTapeController(eScrollDirection direction) : m_direction(direction) {
|
||||
;
|
||||
}
|
||||
|
||||
void CScrollTapeController::setDirection(eScrollDirection dir) {
|
||||
m_direction = dir;
|
||||
}
|
||||
|
||||
eScrollDirection CScrollTapeController::getDirection() const {
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
bool CScrollTapeController::isPrimaryHorizontal() const {
|
||||
return m_direction == SCROLL_DIR_RIGHT || m_direction == SCROLL_DIR_LEFT;
|
||||
}
|
||||
|
||||
bool CScrollTapeController::isReversed() const {
|
||||
return m_direction == SCROLL_DIR_LEFT || m_direction == SCROLL_DIR_UP;
|
||||
}
|
||||
|
||||
size_t CScrollTapeController::stripCount() const {
|
||||
return m_strips.size();
|
||||
}
|
||||
|
||||
SStripData& CScrollTapeController::getStrip(size_t index) {
|
||||
return m_strips[index];
|
||||
}
|
||||
|
||||
const SStripData& CScrollTapeController::getStrip(size_t index) const {
|
||||
return m_strips[index];
|
||||
}
|
||||
|
||||
void CScrollTapeController::setOffset(double offset) {
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
double CScrollTapeController::getOffset() const {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
void CScrollTapeController::adjustOffset(double delta) {
|
||||
m_offset += delta;
|
||||
}
|
||||
|
||||
size_t CScrollTapeController::addStrip(float size) {
|
||||
m_strips.emplace_back();
|
||||
m_strips.back().size = size;
|
||||
return m_strips.size() - 1;
|
||||
}
|
||||
|
||||
void CScrollTapeController::insertStrip(size_t afterIndex, float size) {
|
||||
if (afterIndex >= m_strips.size()) {
|
||||
addStrip(size);
|
||||
return;
|
||||
}
|
||||
|
||||
SStripData newStrip;
|
||||
newStrip.size = size;
|
||||
m_strips.insert(m_strips.begin() + afterIndex + 1, newStrip);
|
||||
}
|
||||
|
||||
void CScrollTapeController::removeStrip(size_t index) {
|
||||
if (index < m_strips.size())
|
||||
m_strips.erase(m_strips.begin() + index);
|
||||
}
|
||||
|
||||
double CScrollTapeController::getPrimary(const Vector2D& v) const {
|
||||
return isPrimaryHorizontal() ? v.x : v.y;
|
||||
}
|
||||
|
||||
double CScrollTapeController::getSecondary(const Vector2D& v) const {
|
||||
return isPrimaryHorizontal() ? v.y : v.x;
|
||||
}
|
||||
|
||||
void CScrollTapeController::setPrimary(Vector2D& v, double val) const {
|
||||
if (isPrimaryHorizontal())
|
||||
v.x = val;
|
||||
else
|
||||
v.y = val;
|
||||
}
|
||||
|
||||
void CScrollTapeController::setSecondary(Vector2D& v, double val) const {
|
||||
if (isPrimaryHorizontal())
|
||||
v.y = val;
|
||||
else
|
||||
v.x = val;
|
||||
}
|
||||
|
||||
Vector2D CScrollTapeController::makeVector(double primary, double secondary) const {
|
||||
if (isPrimaryHorizontal())
|
||||
return {primary, secondary};
|
||||
else
|
||||
return {secondary, primary};
|
||||
}
|
||||
|
||||
double CScrollTapeController::calculateMaxExtent(const CBox& usableArea, bool fullscreenOnOne) const {
|
||||
if (m_strips.empty())
|
||||
return 0.0;
|
||||
|
||||
if (fullscreenOnOne && m_strips.size() == 1)
|
||||
return getPrimary(usableArea.size());
|
||||
|
||||
double total = 0.0;
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
|
||||
for (const auto& strip : m_strips) {
|
||||
total += usablePrimary * strip.size;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
double CScrollTapeController::calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) const {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return 0.0;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
double current = 0.0;
|
||||
|
||||
for (size_t i = 0; i < stripIndex; ++i) {
|
||||
const double stripSize = (fullscreenOnOne && m_strips.size() == 1) ? usablePrimary : usablePrimary * m_strips[i].size;
|
||||
current += stripSize;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
double CScrollTapeController::calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) const {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return 0.0;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
|
||||
if (fullscreenOnOne && m_strips.size() == 1)
|
||||
return usablePrimary;
|
||||
|
||||
return usablePrimary * m_strips[stripIndex].size;
|
||||
}
|
||||
|
||||
CBox CScrollTapeController::calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne) {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return {};
|
||||
|
||||
const auto& strip = m_strips[stripIndex];
|
||||
if (targetIndex >= strip.targetSizes.size())
|
||||
return {};
|
||||
|
||||
const double usableSecondary = getSecondary(usableArea.size());
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
const double cameraOffset = calculateCameraOffset(usableArea, fullscreenOnOne);
|
||||
|
||||
// calculate position along primary axis (strip position)
|
||||
double primaryPos = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
|
||||
double primarySize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
|
||||
|
||||
// calculate position along secondary axis (within strip)
|
||||
double secondaryPos = 0.0;
|
||||
for (size_t i = 0; i < targetIndex; ++i) {
|
||||
secondaryPos += strip.targetSizes[i] * usableSecondary;
|
||||
}
|
||||
double secondarySize = strip.targetSizes[targetIndex] * usableSecondary;
|
||||
|
||||
// apply camera offset based on direction
|
||||
// for RIGHT/DOWN: scroll offset moves content left/up (subtract)
|
||||
// for LEFT/UP: scroll offset moves content right/down (different coordinate system)
|
||||
if (m_direction == SCROLL_DIR_LEFT) {
|
||||
// LEFT: flip the entire primary axis, then apply offset
|
||||
primaryPos = usablePrimary - primaryPos - primarySize + cameraOffset;
|
||||
} else if (m_direction == SCROLL_DIR_UP) {
|
||||
// UP: flip the entire primary axis, then apply offset
|
||||
primaryPos = usablePrimary - primaryPos - primarySize + cameraOffset;
|
||||
} else {
|
||||
// RIGHT/DOWN: normal offset
|
||||
primaryPos -= cameraOffset;
|
||||
}
|
||||
|
||||
// create the box in primary/secondary coordinates
|
||||
Vector2D pos = makeVector(primaryPos, secondaryPos);
|
||||
Vector2D size = makeVector(primarySize, secondarySize);
|
||||
|
||||
// translate to workspace position
|
||||
pos = pos + workspaceOffset;
|
||||
|
||||
return CBox{pos, size};
|
||||
}
|
||||
|
||||
double CScrollTapeController::calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne) {
|
||||
const double maxExtent = calculateMaxExtent(usableArea, fullscreenOnOne);
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
|
||||
// don't adjust the offset if we are dragging
|
||||
if (isBeingDragged())
|
||||
return m_offset;
|
||||
|
||||
// if the content fits in viewport, center it
|
||||
if (maxExtent < usablePrimary)
|
||||
m_offset = std::round((maxExtent - usablePrimary) / 2.0);
|
||||
|
||||
// if the offset is negative but we already extended, reset offset to 0
|
||||
if (maxExtent > usablePrimary && m_offset < 0.0)
|
||||
m_offset = 0.0;
|
||||
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
Vector2D CScrollTapeController::getCameraTranslation(const CBox& usableArea, bool fullscreenOnOne) {
|
||||
const double offset = calculateCameraOffset(usableArea, fullscreenOnOne);
|
||||
|
||||
if (isReversed())
|
||||
return makeVector(offset, 0.0);
|
||||
else
|
||||
return makeVector(-offset, 0.0);
|
||||
}
|
||||
|
||||
void CScrollTapeController::centerStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
|
||||
const double stripSize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
|
||||
|
||||
m_offset = stripStart - (usablePrimary - stripSize) / 2.0;
|
||||
}
|
||||
|
||||
void CScrollTapeController::fitStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
|
||||
const double stripSize = calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
|
||||
|
||||
m_offset = std::clamp(m_offset, stripStart - usablePrimary + stripSize, stripStart);
|
||||
}
|
||||
|
||||
bool CScrollTapeController::isStripVisible(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne) const {
|
||||
if (stripIndex >= m_strips.size())
|
||||
return false;
|
||||
|
||||
const double stripStart = calculateStripStart(stripIndex, usableArea, fullscreenOnOne);
|
||||
const double stripEnd = stripStart + calculateStripSize(stripIndex, usableArea, fullscreenOnOne);
|
||||
const double viewStart = m_offset;
|
||||
const double viewEnd = m_offset + getPrimary(usableArea.size());
|
||||
|
||||
return stripStart < viewEnd && viewStart < stripEnd;
|
||||
}
|
||||
|
||||
size_t CScrollTapeController::getStripAtCenter(const CBox& usableArea, bool fullscreenOnOne) const {
|
||||
if (m_strips.empty())
|
||||
return 0;
|
||||
|
||||
const double usablePrimary = getPrimary(usableArea.size());
|
||||
double currentPos = m_offset;
|
||||
|
||||
for (size_t i = 0; i < m_strips.size(); ++i) {
|
||||
const double stripSize = calculateStripSize(i, usableArea, fullscreenOnOne);
|
||||
currentPos += stripSize;
|
||||
|
||||
if (currentPos >= usablePrimary / 2.0 - 2.0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return m_strips.empty() ? 0 : m_strips.size() - 1;
|
||||
}
|
||||
|
||||
void CScrollTapeController::swapStrips(size_t a, size_t b) {
|
||||
if (a >= m_strips.size() || b >= m_strips.size())
|
||||
return;
|
||||
|
||||
std::swap(m_strips.at(a), m_strips.at(b));
|
||||
}
|
||||
|
||||
bool CScrollTapeController::isBeingDragged() const {
|
||||
for (const auto& s : m_strips) {
|
||||
if (!s.userData)
|
||||
continue;
|
||||
|
||||
for (const auto& d : s.userData->targetDatas) {
|
||||
if (d->target == g_layoutManager->dragController()->target())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../../../helpers/math/Math.hpp"
|
||||
#include "../../../../helpers/memory/Memory.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace Layout::Tiled {
|
||||
|
||||
struct SColumnData;
|
||||
|
||||
enum eScrollDirection : uint8_t {
|
||||
SCROLL_DIR_RIGHT = 0,
|
||||
SCROLL_DIR_LEFT,
|
||||
SCROLL_DIR_DOWN,
|
||||
SCROLL_DIR_UP,
|
||||
};
|
||||
|
||||
struct SStripData {
|
||||
float size = 1.F; // size along primary axis
|
||||
std::vector<float> targetSizes; // sizes along secondary axis for each target in this strip
|
||||
WP<SColumnData> userData;
|
||||
|
||||
SStripData() = default;
|
||||
};
|
||||
|
||||
struct STapeLayoutResult {
|
||||
CBox box;
|
||||
size_t stripIndex = 0;
|
||||
size_t targetIndex = 0;
|
||||
};
|
||||
|
||||
class CScrollTapeController {
|
||||
public:
|
||||
CScrollTapeController(eScrollDirection direction = SCROLL_DIR_RIGHT);
|
||||
~CScrollTapeController() = default;
|
||||
|
||||
void setDirection(eScrollDirection dir);
|
||||
eScrollDirection getDirection() const;
|
||||
bool isPrimaryHorizontal() const;
|
||||
bool isReversed() const;
|
||||
|
||||
size_t addStrip(float size = 1.0F);
|
||||
void insertStrip(size_t afterIndex, float size = 1.0F);
|
||||
void removeStrip(size_t index);
|
||||
size_t stripCount() const;
|
||||
SStripData& getStrip(size_t index);
|
||||
const SStripData& getStrip(size_t index) const;
|
||||
void swapStrips(size_t a, size_t b);
|
||||
|
||||
void setOffset(double offset);
|
||||
double getOffset() const;
|
||||
void adjustOffset(double delta);
|
||||
|
||||
double calculateMaxExtent(const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
double calculateStripStart(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
double calculateStripSize(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
|
||||
CBox calculateTargetBox(size_t stripIndex, size_t targetIndex, const CBox& usableArea, const Vector2D& workspaceOffset, bool fullscreenOnOne = false);
|
||||
|
||||
double calculateCameraOffset(const CBox& usableArea, bool fullscreenOnOne = false);
|
||||
Vector2D getCameraTranslation(const CBox& usableArea, bool fullscreenOnOne = false);
|
||||
|
||||
void centerStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false);
|
||||
void fitStrip(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false);
|
||||
|
||||
bool isStripVisible(size_t stripIndex, const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
|
||||
size_t getStripAtCenter(const CBox& usableArea, bool fullscreenOnOne = false) const;
|
||||
|
||||
private:
|
||||
eScrollDirection m_direction = SCROLL_DIR_RIGHT;
|
||||
std::vector<SStripData> m_strips;
|
||||
double m_offset = 0.0;
|
||||
|
||||
double getPrimary(const Vector2D& v) const;
|
||||
double getSecondary(const Vector2D& v) const;
|
||||
void setPrimary(Vector2D& v, double val) const;
|
||||
void setSecondary(Vector2D& v, double val) const;
|
||||
bool isBeingDragged() const;
|
||||
|
||||
Vector2D makeVector(double primary, double secondary) const;
|
||||
};
|
||||
};
|
||||
1412
src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
Normal file
1412
src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.cpp
Normal file
File diff suppressed because it is too large
Load diff
137
src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
Normal file
137
src/layout/algorithm/tiled/scrolling/ScrollingAlgorithm.hpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../TiledAlgorithm.hpp"
|
||||
#include "../../../../managers/HookSystemManager.hpp"
|
||||
#include "../../../../helpers/math/Direction.hpp"
|
||||
#include "ScrollTapeController.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Layout::Tiled {
|
||||
class CScrollingAlgorithm;
|
||||
struct SColumnData;
|
||||
struct SScrollingData;
|
||||
|
||||
struct SScrollingTargetData {
|
||||
SScrollingTargetData(SP<ITarget> t, SP<SColumnData> col) : target(t), column(col) {
|
||||
;
|
||||
}
|
||||
|
||||
WP<ITarget> target;
|
||||
WP<SColumnData> column;
|
||||
bool ignoreFullscreenChecks = false;
|
||||
|
||||
CBox layoutBox;
|
||||
};
|
||||
|
||||
struct SColumnData {
|
||||
SColumnData(SP<SScrollingData> data) : scrollingData(data) {
|
||||
;
|
||||
}
|
||||
|
||||
void add(SP<ITarget> t);
|
||||
void add(SP<ITarget> t, int after);
|
||||
void add(SP<SScrollingTargetData> w);
|
||||
void add(SP<SScrollingTargetData> w, int after);
|
||||
void remove(SP<ITarget> t);
|
||||
bool has(SP<ITarget> t);
|
||||
size_t idx(SP<ITarget> t);
|
||||
|
||||
// index of lowest target that is above y.
|
||||
size_t idxForHeight(float y);
|
||||
|
||||
void up(SP<SScrollingTargetData> w);
|
||||
void down(SP<SScrollingTargetData> w);
|
||||
|
||||
SP<SScrollingTargetData> next(SP<SScrollingTargetData> w);
|
||||
SP<SScrollingTargetData> prev(SP<SScrollingTargetData> w);
|
||||
|
||||
std::vector<SP<SScrollingTargetData>> targetDatas;
|
||||
WP<SScrollingData> scrollingData;
|
||||
WP<SScrollingTargetData> lastFocusedTarget;
|
||||
|
||||
WP<SColumnData> self;
|
||||
|
||||
// Helper methods to access controller-managed data
|
||||
float getColumnWidth() const;
|
||||
void setColumnWidth(float width);
|
||||
float getTargetSize(size_t idx) const;
|
||||
void setTargetSize(size_t idx, float size);
|
||||
float getTargetSize(SP<SScrollingTargetData> target) const;
|
||||
void setTargetSize(SP<SScrollingTargetData> target, float size);
|
||||
};
|
||||
|
||||
struct SScrollingData {
|
||||
SScrollingData(CScrollingAlgorithm* algo);
|
||||
|
||||
std::vector<SP<SColumnData>> columns;
|
||||
|
||||
UP<CScrollTapeController> controller;
|
||||
|
||||
SP<SColumnData> add();
|
||||
SP<SColumnData> add(int after);
|
||||
int64_t idx(SP<SColumnData> c);
|
||||
void remove(SP<SColumnData> c);
|
||||
double maxWidth();
|
||||
SP<SColumnData> next(SP<SColumnData> c);
|
||||
SP<SColumnData> prev(SP<SColumnData> c);
|
||||
SP<SColumnData> atCenter();
|
||||
|
||||
bool visible(SP<SColumnData> c);
|
||||
void centerCol(SP<SColumnData> c);
|
||||
void fitCol(SP<SColumnData> c);
|
||||
void centerOrFitCol(SP<SColumnData> c);
|
||||
|
||||
void recalculate(bool forceInstant = false);
|
||||
|
||||
CScrollingAlgorithm* algorithm = nullptr;
|
||||
WP<SScrollingData> self;
|
||||
std::optional<double> lockedCameraOffset;
|
||||
};
|
||||
|
||||
class CScrollingAlgorithm : public ITiledAlgorithm {
|
||||
public:
|
||||
CScrollingAlgorithm();
|
||||
virtual ~CScrollingAlgorithm();
|
||||
|
||||
virtual void newTarget(SP<ITarget> target);
|
||||
virtual void movedTarget(SP<ITarget> target, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void removeTarget(SP<ITarget> target);
|
||||
|
||||
virtual void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
virtual void recalculate();
|
||||
|
||||
virtual SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
virtual std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
virtual std::optional<Vector2D> predictSizeForNewTarget();
|
||||
|
||||
virtual void swapTargets(SP<ITarget> a, SP<ITarget> b);
|
||||
virtual void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
CBox usableArea();
|
||||
|
||||
private:
|
||||
SP<SScrollingData> m_scrollingData;
|
||||
|
||||
SP<HOOK_CALLBACK_FN> m_configCallback;
|
||||
SP<HOOK_CALLBACK_FN> m_focusCallback;
|
||||
SP<HOOK_CALLBACK_FN> m_mouseButtonCallback;
|
||||
|
||||
struct {
|
||||
std::vector<float> configuredWidths;
|
||||
} m_config;
|
||||
|
||||
eScrollDirection getDynamicDirection();
|
||||
|
||||
SP<SScrollingTargetData> findBestNeighbor(SP<SScrollingTargetData> pCurrent, SP<SColumnData> pTargetCol);
|
||||
SP<SScrollingTargetData> dataFor(SP<ITarget> t);
|
||||
SP<SScrollingTargetData> closestNode(const Vector2D& posGlobglobgabgalab);
|
||||
|
||||
void focusTargetUpdate(SP<ITarget> target);
|
||||
void moveTargetTo(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
void focusOnInput(SP<ITarget> target, bool hardInput);
|
||||
|
||||
friend struct SScrollingData;
|
||||
};
|
||||
};
|
||||
185
src/layout/space/Space.cpp
Normal file
185
src/layout/space/Space.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "Space.hpp"
|
||||
|
||||
#include "../target/Target.hpp"
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
|
||||
#include "../../debug/log/Logger.hpp"
|
||||
#include "../../desktop/Workspace.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<CSpace> CSpace::create(PHLWORKSPACE w) {
|
||||
auto space = SP<CSpace>(new CSpace(w));
|
||||
space->m_self = space;
|
||||
return space;
|
||||
}
|
||||
|
||||
CSpace::CSpace(PHLWORKSPACE parent) : m_parent(parent) {
|
||||
recheckWorkArea();
|
||||
}
|
||||
|
||||
void CSpace::add(SP<ITarget> t) {
|
||||
m_targets.emplace_back(t);
|
||||
|
||||
recheckWorkArea();
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->addTarget(t);
|
||||
|
||||
m_parent->updateWindows();
|
||||
}
|
||||
|
||||
void CSpace::move(SP<ITarget> t, std::optional<Vector2D> focalPoint) {
|
||||
m_targets.emplace_back(t);
|
||||
|
||||
recheckWorkArea();
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->moveTarget(t, focalPoint);
|
||||
|
||||
m_parent->updateWindows();
|
||||
}
|
||||
|
||||
void CSpace::remove(SP<ITarget> t) {
|
||||
std::erase_if(m_targets, [&t](const auto& e) { return !e || e == t; });
|
||||
|
||||
recheckWorkArea();
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->removeTarget(t);
|
||||
|
||||
if (m_parent) // can be null if the workspace is gone
|
||||
m_parent->updateWindows();
|
||||
}
|
||||
|
||||
void CSpace::setAlgorithmProvider(SP<CAlgorithm> algo) {
|
||||
m_algorithm = algo;
|
||||
}
|
||||
|
||||
void CSpace::recheckWorkArea() {
|
||||
if (!m_parent || !m_parent->m_monitor) {
|
||||
Log::logger->log(Log::ERR, "CSpace: recheckWorkArea on no parent / mon?!");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(m_parent.lock());
|
||||
|
||||
auto workArea = m_parent->m_monitor->logicalBoxMinusReserved();
|
||||
|
||||
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
|
||||
static auto PFLOATGAPSDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:float_gaps");
|
||||
auto* const PGAPSOUT = sc<CCssGapData*>((PGAPSOUTDATA.ptr())->getData());
|
||||
auto* PFLOATGAPS = sc<CCssGapData*>(PFLOATGAPSDATA.ptr()->getData());
|
||||
if (PFLOATGAPS->m_bottom < 0 || PFLOATGAPS->m_left < 0 || PFLOATGAPS->m_right < 0 || PFLOATGAPS->m_top < 0)
|
||||
PFLOATGAPS = PGAPSOUT;
|
||||
|
||||
auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
|
||||
auto gapsFloat = WORKSPACERULE.gapsOut.value_or(*PFLOATGAPS);
|
||||
|
||||
Desktop::CReservedArea reservedGaps{gapsOut.m_top, gapsOut.m_right, gapsOut.m_bottom, gapsOut.m_left};
|
||||
Desktop::CReservedArea reservedFloatGaps{gapsFloat.m_top, gapsFloat.m_right, gapsFloat.m_bottom, gapsFloat.m_left};
|
||||
|
||||
auto floatWorkArea = workArea;
|
||||
|
||||
reservedFloatGaps.applyip(floatWorkArea);
|
||||
reservedGaps.applyip(workArea);
|
||||
|
||||
m_workArea = workArea;
|
||||
m_floatingWorkArea = floatWorkArea;
|
||||
}
|
||||
|
||||
const CBox& CSpace::workArea(bool floating) const {
|
||||
return floating ? m_floatingWorkArea : m_workArea;
|
||||
}
|
||||
|
||||
PHLWORKSPACE CSpace::workspace() const {
|
||||
return m_parent.lock();
|
||||
}
|
||||
|
||||
void CSpace::toggleTargetFloating(SP<ITarget> t) {
|
||||
t->setWasTiling(true);
|
||||
m_algorithm->setFloating(t, !t->floating());
|
||||
t->setWasTiling(false);
|
||||
|
||||
m_parent->updateWindows();
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
CBox CSpace::targetPositionLocal(SP<ITarget> t) const {
|
||||
return t->position().translate(-m_workArea.pos());
|
||||
}
|
||||
|
||||
void CSpace::resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner) {
|
||||
if (!m_algorithm)
|
||||
return;
|
||||
|
||||
m_algorithm->resizeTarget(Δ, target, corner);
|
||||
}
|
||||
|
||||
void CSpace::moveTarget(const Vector2D& Δ, SP<ITarget> target) {
|
||||
if (!m_algorithm)
|
||||
return;
|
||||
|
||||
m_algorithm->moveTarget(Δ, target);
|
||||
}
|
||||
|
||||
SP<CAlgorithm> CSpace::algorithm() const {
|
||||
return m_algorithm;
|
||||
}
|
||||
|
||||
void CSpace::recalculate() {
|
||||
recheckWorkArea();
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->recalculate();
|
||||
}
|
||||
|
||||
void CSpace::setFullscreen(SP<ITarget> t, eFullscreenMode mode) {
|
||||
t->setFullscreenMode(mode);
|
||||
|
||||
if (mode == FSMODE_NONE && m_algorithm && t->floating())
|
||||
m_algorithm->recenter(t);
|
||||
|
||||
recalculate();
|
||||
}
|
||||
|
||||
std::expected<void, std::string> CSpace::layoutMsg(const std::string_view& sv) {
|
||||
if (m_algorithm)
|
||||
return m_algorithm->layoutMsg(sv);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CSpace::predictSizeForNewTiledTarget() {
|
||||
if (m_algorithm)
|
||||
return m_algorithm->predictSizeForNewTiledTarget();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void CSpace::swap(SP<ITarget> a, SP<ITarget> b) {
|
||||
for (auto& t : m_targets) {
|
||||
if (t == a)
|
||||
t = b;
|
||||
else if (t == b)
|
||||
t = a;
|
||||
}
|
||||
|
||||
if (m_algorithm)
|
||||
m_algorithm->swapTargets(a, b);
|
||||
}
|
||||
|
||||
void CSpace::moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent) {
|
||||
if (m_algorithm)
|
||||
m_algorithm->moveTargetInDirection(t, dir, silent);
|
||||
}
|
||||
|
||||
SP<ITarget> CSpace::getNextCandidate(SP<ITarget> old) {
|
||||
return !m_algorithm ? nullptr : m_algorithm->getNextCandidate(old);
|
||||
}
|
||||
|
||||
const std::vector<WP<ITarget>>& CSpace::targets() const {
|
||||
return m_targets;
|
||||
}
|
||||
67
src/layout/space/Space.hpp
Normal file
67
src/layout/space/Space.hpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/math/Direction.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
|
||||
#include "../../desktop/DesktopTypes.hpp"
|
||||
|
||||
#include "../LayoutManager.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <expected>
|
||||
|
||||
namespace Layout {
|
||||
class ITarget;
|
||||
class CAlgorithm;
|
||||
|
||||
class CSpace {
|
||||
public:
|
||||
static SP<CSpace> create(PHLWORKSPACE w);
|
||||
~CSpace() = default;
|
||||
|
||||
void add(SP<ITarget> t);
|
||||
void remove(SP<ITarget> t);
|
||||
void move(SP<ITarget> t, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
|
||||
void swap(SP<ITarget> a, SP<ITarget> b);
|
||||
|
||||
SP<ITarget> getNextCandidate(SP<ITarget> old);
|
||||
|
||||
void setAlgorithmProvider(SP<CAlgorithm> algo);
|
||||
void recheckWorkArea();
|
||||
void setFullscreen(SP<ITarget> t, eFullscreenMode mode);
|
||||
|
||||
void moveTargetInDirection(SP<ITarget> t, Math::eDirection dir, bool silent);
|
||||
|
||||
void recalculate();
|
||||
|
||||
void toggleTargetFloating(SP<ITarget> t);
|
||||
|
||||
std::expected<void, std::string> layoutMsg(const std::string_view& sv);
|
||||
std::optional<Vector2D> predictSizeForNewTiledTarget();
|
||||
|
||||
const CBox& workArea(bool floating = false) const;
|
||||
PHLWORKSPACE workspace() const;
|
||||
CBox targetPositionLocal(SP<ITarget> t) const;
|
||||
|
||||
void resizeTarget(const Vector2D& Δ, SP<ITarget> target, eRectCorner corner = CORNER_NONE);
|
||||
void moveTarget(const Vector2D& Δ, SP<ITarget> target);
|
||||
|
||||
SP<CAlgorithm> algorithm() const;
|
||||
|
||||
const std::vector<WP<ITarget>>& targets() const;
|
||||
|
||||
private:
|
||||
CSpace(PHLWORKSPACE parent);
|
||||
|
||||
WP<CSpace> m_self;
|
||||
|
||||
std::vector<WP<ITarget>> m_targets;
|
||||
SP<CAlgorithm> m_algorithm;
|
||||
PHLWORKSPACEREF m_parent;
|
||||
|
||||
// work area is in global coords
|
||||
CBox m_workArea, m_floatingWorkArea;
|
||||
};
|
||||
};
|
||||
396
src/layout/supplementary/DragController.cpp
Normal file
396
src/layout/supplementary/DragController.cpp
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
#include "DragController.hpp"
|
||||
|
||||
#include "../space/Space.hpp"
|
||||
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../managers/cursor/CursorShapeOverrideController.hpp"
|
||||
#include "../../desktop/state/FocusState.hpp"
|
||||
#include "../../desktop/view/Group.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Layout::Supplementary;
|
||||
|
||||
SP<ITarget> CDragStateController::target() const {
|
||||
return m_target.lock();
|
||||
}
|
||||
|
||||
eMouseBindMode CDragStateController::mode() const {
|
||||
return m_dragMode;
|
||||
}
|
||||
|
||||
bool CDragStateController::wasDraggingWindow() const {
|
||||
return m_wasDraggingWindow;
|
||||
}
|
||||
|
||||
bool CDragStateController::dragThresholdReached() const {
|
||||
return m_dragThresholdReached;
|
||||
}
|
||||
|
||||
void CDragStateController::resetDragThresholdReached() {
|
||||
m_dragThresholdReached = false;
|
||||
}
|
||||
|
||||
bool CDragStateController::draggingTiled() const {
|
||||
return m_draggingTiled;
|
||||
}
|
||||
|
||||
bool CDragStateController::updateDragWindow() {
|
||||
const auto DRAGGINGTARGET = m_target.lock();
|
||||
const bool WAS_FULLSCREEN = DRAGGINGTARGET->fullscreenMode() != FSMODE_NONE;
|
||||
|
||||
if (m_dragThresholdReached) {
|
||||
if (WAS_FULLSCREEN) {
|
||||
Log::logger->log(Log::DEBUG, "Dragging a fullscreen window");
|
||||
g_pCompositor->setWindowFullscreenInternal(DRAGGINGTARGET->window(), FSMODE_NONE);
|
||||
}
|
||||
|
||||
const auto PWORKSPACE = DRAGGINGTARGET->workspace();
|
||||
const auto DRAGGINGWINDOW = DRAGGINGTARGET->window();
|
||||
|
||||
if (PWORKSPACE->m_hasFullscreenWindow && (!DRAGGINGTARGET->floating() || (!DRAGGINGWINDOW->m_createdOverFullscreen && !DRAGGINGWINDOW->m_pinned))) {
|
||||
Log::logger->log(Log::DEBUG, "Rejecting drag on a fullscreen workspace. (window under fullscreen)");
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_draggingTiled = false;
|
||||
m_draggingWindowOriginalFloatSize = DRAGGINGTARGET->lastFloatingSize();
|
||||
|
||||
if (WAS_FULLSCREEN && DRAGGINGTARGET->floating()) {
|
||||
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
||||
DRAGGINGTARGET->setPositionGlobal(CBox{MOUSECOORDS - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()});
|
||||
} else if (!DRAGGINGTARGET->floating() && m_dragMode == MBIND_MOVE) {
|
||||
Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE});
|
||||
DRAGGINGTARGET->rememberFloatingSize((DRAGGINGTARGET->position().size() * 0.8489).clamp(MINSIZE, Vector2D{}).floor());
|
||||
DRAGGINGTARGET->setPositionGlobal(CBox{g_pInputManager->getMouseCoordsInternal() - DRAGGINGTARGET->position().size() / 2.F, DRAGGINGTARGET->position().size()});
|
||||
|
||||
if (m_dragThresholdReached) {
|
||||
g_layoutManager->changeFloatingMode(DRAGGINGTARGET);
|
||||
m_draggingTiled = true;
|
||||
}
|
||||
}
|
||||
|
||||
const auto DRAG_ORIGINAL_BOX = DRAGGINGTARGET->position();
|
||||
|
||||
m_beginDragXY = g_pInputManager->getMouseCoordsInternal();
|
||||
m_beginDragPositionXY = DRAG_ORIGINAL_BOX.pos();
|
||||
m_beginDragSizeXY = DRAG_ORIGINAL_BOX.size();
|
||||
m_lastDragXY = m_beginDragXY;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDragStateController::dragBegin(SP<ITarget> target, eMouseBindMode mode) {
|
||||
m_target = target;
|
||||
m_dragMode = mode;
|
||||
|
||||
const auto DRAGGINGTARGET = m_target.lock();
|
||||
static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
|
||||
|
||||
m_mouseMoveEventCount = 1;
|
||||
m_beginDragSizeXY = Vector2D();
|
||||
|
||||
// Window will be floating. Let's check if it's valid. It should be, but I don't like crashing.
|
||||
if (!validMapped(DRAGGINGTARGET->window())) {
|
||||
Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (not mapped)");
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DRAGGINGTARGET->workspace()) {
|
||||
Log::logger->log(Log::ERR, "Dragging attempted on an invalid window (no workspace)");
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to pick up dragged window now if drag_threshold is disabled
|
||||
// or at least update dragging related variables for the cursors
|
||||
m_dragThresholdReached = *PDRAGTHRESHOLD <= 0;
|
||||
if (updateDragWindow())
|
||||
return;
|
||||
|
||||
// get the grab corner
|
||||
static auto RESIZECORNER = CConfigValue<Hyprlang::INT>("general:resize_corner");
|
||||
if (*RESIZECORNER != 0 && *RESIZECORNER <= 4 && DRAGGINGTARGET->floating()) {
|
||||
switch (*RESIZECORNER) {
|
||||
case 1:
|
||||
m_grabbedCorner = CORNER_TOPLEFT;
|
||||
Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
break;
|
||||
case 2:
|
||||
m_grabbedCorner = CORNER_TOPRIGHT;
|
||||
Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
break;
|
||||
case 3:
|
||||
m_grabbedCorner = CORNER_BOTTOMRIGHT;
|
||||
Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
break;
|
||||
case 4:
|
||||
m_grabbedCorner = CORNER_BOTTOMLEFT;
|
||||
Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
break;
|
||||
}
|
||||
} else if (m_beginDragXY.x < m_beginDragPositionXY.x + m_beginDragSizeXY.x / 2.F) {
|
||||
if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) {
|
||||
m_grabbedCorner = CORNER_TOPLEFT;
|
||||
Cursor::overrideController->setOverride("nw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
} else {
|
||||
m_grabbedCorner = CORNER_BOTTOMLEFT;
|
||||
Cursor::overrideController->setOverride("sw-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
}
|
||||
} else {
|
||||
if (m_beginDragXY.y < m_beginDragPositionXY.y + m_beginDragSizeXY.y / 2.F) {
|
||||
m_grabbedCorner = CORNER_TOPRIGHT;
|
||||
Cursor::overrideController->setOverride("ne-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
} else {
|
||||
m_grabbedCorner = CORNER_BOTTOMRIGHT;
|
||||
Cursor::overrideController->setOverride("se-resize", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dragMode != MBIND_RESIZE && m_dragMode != MBIND_RESIZE_FORCE_RATIO && m_dragMode != MBIND_RESIZE_BLOCK_RATIO)
|
||||
Cursor::overrideController->setOverride("grabbing", Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
|
||||
DRAGGINGTARGET->damageEntire();
|
||||
|
||||
g_pKeybindManager->shadowKeybinds();
|
||||
|
||||
if (DRAGGINGTARGET->window()) {
|
||||
Desktop::focusState()->rawWindowFocus(DRAGGINGTARGET->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
g_pCompositor->changeWindowZOrder(DRAGGINGTARGET->window(), true);
|
||||
}
|
||||
}
|
||||
void CDragStateController::dragEnd() {
|
||||
auto draggingTarget = m_target.lock();
|
||||
|
||||
m_mouseMoveEventCount = 1;
|
||||
|
||||
if (!validMapped(draggingTarget->window())) {
|
||||
if (draggingTarget->window()) {
|
||||
Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
m_target.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Cursor::overrideController->unsetOverride(Cursor::CURSOR_OVERRIDE_SPECIAL_ACTION);
|
||||
m_target.reset();
|
||||
m_wasDraggingWindow = true;
|
||||
|
||||
if (m_dragMode == MBIND_MOVE && draggingTarget->window()) {
|
||||
draggingTarget->damageEntire();
|
||||
|
||||
const auto DRAGGING_WINDOW = draggingTarget->window();
|
||||
|
||||
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
||||
PHLWINDOW pWindow =
|
||||
g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGING_WINDOW);
|
||||
|
||||
if (pWindow) {
|
||||
if (pWindow->checkInputOnDecos(INPUT_TYPE_DRAG_END, MOUSECOORDS, DRAGGING_WINDOW))
|
||||
return;
|
||||
|
||||
const bool FLOATEDINTOTILED = !pWindow->m_isFloating && !m_draggingTiled;
|
||||
static auto PDRAGINTOGROUP = CConfigValue<Hyprlang::INT>("group:drag_into_group");
|
||||
|
||||
if (pWindow->m_group && DRAGGING_WINDOW->canBeGroupedInto(pWindow->m_group) && *PDRAGINTOGROUP == 1 && !FLOATEDINTOTILED) {
|
||||
pWindow->m_group->add(DRAGGING_WINDOW);
|
||||
// fix the draggingTarget, now it's DRAGGING_WINDOW
|
||||
draggingTarget = DRAGGING_WINDOW->m_target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_draggingTiled) {
|
||||
// static auto PPRECISEMOUSE = CConfigValue<Hyprlang::INT>("dwindle:precise_mouse_move");
|
||||
|
||||
// FIXME: remove or rethink
|
||||
// if (*PPRECISEMOUSE) {
|
||||
// eDirection direction = DIRECTION_DEFAULT;
|
||||
|
||||
// const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
||||
// const PHLWINDOW pReferenceWindow =
|
||||
// g_pCompositor->vectorToWindowUnified(MOUSECOORDS, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, DRAGGINGWINDOW);
|
||||
|
||||
// if (pReferenceWindow && pReferenceWindow != DRAGGINGWINDOW) {
|
||||
// const Vector2D draggedCenter = DRAGGINGWINDOW->m_realPosition->goal() + DRAGGINGWINDOW->m_realSize->goal() / 2.f;
|
||||
// const Vector2D referenceCenter = pReferenceWindow->m_realPosition->goal() + pReferenceWindow->m_realSize->goal() / 2.f;
|
||||
// const float xDiff = draggedCenter.x - referenceCenter.x;
|
||||
// const float yDiff = draggedCenter.y - referenceCenter.y;
|
||||
|
||||
// if (fabs(xDiff) > fabs(yDiff))
|
||||
// direction = xDiff < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
|
||||
// else
|
||||
// direction = yDiff < 0 ? DIRECTION_UP : DIRECTION_DOWN;
|
||||
// }
|
||||
|
||||
// onWindowRemovedTiling(DRAGGINGWINDOW);
|
||||
// onWindowCreatedTiling(DRAGGINGWINDOW, direction);
|
||||
// } else
|
||||
|
||||
// make sure to check if we are floating because drag into group could make us tiled already
|
||||
if (draggingTarget->floating())
|
||||
g_layoutManager->changeFloatingMode(draggingTarget);
|
||||
|
||||
draggingTarget->rememberFloatingSize(m_draggingWindowOriginalFloatSize);
|
||||
}
|
||||
|
||||
draggingTarget->damageEntire();
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(draggingTarget->window(), Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);
|
||||
|
||||
m_wasDraggingWindow = false;
|
||||
m_dragMode = MBIND_INVALID;
|
||||
}
|
||||
|
||||
void CDragStateController::mouseMove(const Vector2D& mousePos) {
|
||||
if (m_target.expired())
|
||||
return;
|
||||
|
||||
const auto DRAGGINGTARGET = m_target.lock();
|
||||
static auto PDRAGTHRESHOLD = CConfigValue<Hyprlang::INT>("binds:drag_threshold");
|
||||
|
||||
// Window invalid or drag begin size 0,0 meaning we rejected it.
|
||||
if ((!validMapped(DRAGGINGTARGET->window()) || m_beginDragSizeXY == Vector2D())) {
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Yoink dragged window here instead if using drag_threshold and it has been reached
|
||||
if (*PDRAGTHRESHOLD > 0 && !m_dragThresholdReached) {
|
||||
if ((m_beginDragXY.distanceSq(mousePos) <= std::pow(*PDRAGTHRESHOLD, 2) && m_beginDragXY == m_lastDragXY))
|
||||
return;
|
||||
m_dragThresholdReached = true;
|
||||
if (updateDragWindow())
|
||||
return;
|
||||
}
|
||||
|
||||
static auto TIMER = std::chrono::high_resolution_clock::now(), MSTIMER = TIMER;
|
||||
|
||||
const auto DELTA = Vector2D(mousePos.x - m_beginDragXY.x, mousePos.y - m_beginDragXY.y);
|
||||
const auto TICKDELTA = Vector2D(mousePos.x - m_lastDragXY.x, mousePos.y - m_lastDragXY.y);
|
||||
|
||||
static auto SNAPENABLED = CConfigValue<Hyprlang::INT>("general:snap:enabled");
|
||||
|
||||
const auto TIMERDELTA = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - TIMER).count();
|
||||
const auto MSDELTA = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - MSTIMER).count();
|
||||
const auto MSMONITOR = 1000.0 / g_pHyprRenderer->m_mostHzMonitor->m_refreshRate;
|
||||
static int totalMs = 0;
|
||||
bool canSkipUpdate = true;
|
||||
|
||||
MSTIMER = std::chrono::high_resolution_clock::now();
|
||||
|
||||
if (m_mouseMoveEventCount == 1)
|
||||
totalMs = 0;
|
||||
|
||||
if (MSMONITOR > 16.0) {
|
||||
totalMs += MSDELTA < MSMONITOR ? MSDELTA : std::round(totalMs * 1.0 / m_mouseMoveEventCount);
|
||||
m_mouseMoveEventCount += 1;
|
||||
|
||||
// check if time-window is enough to skip update on 60hz monitor
|
||||
canSkipUpdate = std::clamp(MSMONITOR - TIMERDELTA, 0.0, MSMONITOR) > totalMs * 1.0 / m_mouseMoveEventCount;
|
||||
}
|
||||
|
||||
if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) || (TIMERDELTA < MSMONITOR && canSkipUpdate && (m_dragMode != MBIND_MOVE)))
|
||||
return;
|
||||
|
||||
TIMER = std::chrono::high_resolution_clock::now();
|
||||
|
||||
m_lastDragXY = mousePos;
|
||||
|
||||
DRAGGINGTARGET->damageEntire();
|
||||
|
||||
if (m_dragMode == MBIND_MOVE) {
|
||||
|
||||
Vector2D newPos = m_beginDragPositionXY + DELTA;
|
||||
Vector2D newSize = DRAGGINGTARGET->position().size();
|
||||
|
||||
if (*SNAPENABLED && !m_draggingTiled)
|
||||
g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, MBIND_MOVE, -1, m_beginDragSizeXY);
|
||||
|
||||
newPos = newPos.round();
|
||||
|
||||
DRAGGINGTARGET->setPositionGlobal({newPos, newSize});
|
||||
DRAGGINGTARGET->warpPositionSize();
|
||||
} else if (m_dragMode == MBIND_RESIZE || m_dragMode == MBIND_RESIZE_FORCE_RATIO || m_dragMode == MBIND_RESIZE_BLOCK_RATIO) {
|
||||
if (DRAGGINGTARGET->floating()) {
|
||||
|
||||
Vector2D MINSIZE = DRAGGINGTARGET->minSize().value_or(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE});
|
||||
Vector2D MAXSIZE = DRAGGINGTARGET->maxSize().value_or(Math::VECTOR2D_MAX);
|
||||
|
||||
Vector2D newSize = m_beginDragSizeXY;
|
||||
Vector2D newPos = m_beginDragPositionXY;
|
||||
|
||||
if (m_grabbedCorner == CORNER_BOTTOMRIGHT)
|
||||
newSize = newSize + DELTA;
|
||||
else if (m_grabbedCorner == CORNER_TOPLEFT)
|
||||
newSize = newSize - DELTA;
|
||||
else if (m_grabbedCorner == CORNER_TOPRIGHT)
|
||||
newSize = newSize + Vector2D(DELTA.x, -DELTA.y);
|
||||
else if (m_grabbedCorner == CORNER_BOTTOMLEFT)
|
||||
newSize = newSize + Vector2D(-DELTA.x, DELTA.y);
|
||||
|
||||
eMouseBindMode mode = m_dragMode;
|
||||
if (DRAGGINGTARGET->window() && DRAGGINGTARGET->window()->m_ruleApplicator->keepAspectRatio().valueOrDefault() && mode != MBIND_RESIZE_BLOCK_RATIO)
|
||||
mode = MBIND_RESIZE_FORCE_RATIO;
|
||||
|
||||
if (m_beginDragSizeXY.x >= 1 && m_beginDragSizeXY.y >= 1 && mode == MBIND_RESIZE_FORCE_RATIO) {
|
||||
|
||||
const float RATIO = m_beginDragSizeXY.y / m_beginDragSizeXY.x;
|
||||
|
||||
if (MINSIZE.x * RATIO > MINSIZE.y)
|
||||
MINSIZE = Vector2D(MINSIZE.x, MINSIZE.x * RATIO);
|
||||
else
|
||||
MINSIZE = Vector2D(MINSIZE.y / RATIO, MINSIZE.y);
|
||||
|
||||
if (MAXSIZE.x * RATIO < MAXSIZE.y)
|
||||
MAXSIZE = Vector2D(MAXSIZE.x, MAXSIZE.x * RATIO);
|
||||
else
|
||||
MAXSIZE = Vector2D(MAXSIZE.y / RATIO, MAXSIZE.y);
|
||||
|
||||
if (newSize.x * RATIO > newSize.y)
|
||||
newSize = Vector2D(newSize.x, newSize.x * RATIO);
|
||||
else
|
||||
newSize = Vector2D(newSize.y / RATIO, newSize.y);
|
||||
}
|
||||
|
||||
newSize = newSize.clamp(MINSIZE, MAXSIZE);
|
||||
|
||||
if (m_grabbedCorner == CORNER_TOPLEFT)
|
||||
newPos = newPos - newSize + m_beginDragSizeXY;
|
||||
else if (m_grabbedCorner == CORNER_TOPRIGHT)
|
||||
newPos = newPos + Vector2D(0.0, (m_beginDragSizeXY - newSize).y);
|
||||
else if (m_grabbedCorner == CORNER_BOTTOMLEFT)
|
||||
newPos = newPos + Vector2D((m_beginDragSizeXY - newSize).x, 0.0);
|
||||
|
||||
if (*SNAPENABLED) {
|
||||
g_layoutManager->performSnap(newPos, newSize, DRAGGINGTARGET, mode, m_grabbedCorner, m_beginDragSizeXY);
|
||||
newSize = newSize.clamp(MINSIZE, MAXSIZE);
|
||||
}
|
||||
|
||||
CBox wb = {newPos, newSize};
|
||||
wb.round();
|
||||
|
||||
DRAGGINGTARGET->setPositionGlobal(wb);
|
||||
DRAGGINGTARGET->warpPositionSize();
|
||||
} else {
|
||||
g_layoutManager->resizeTarget(TICKDELTA, DRAGGINGTARGET, m_grabbedCorner);
|
||||
DRAGGINGTARGET->warpPositionSize();
|
||||
}
|
||||
}
|
||||
|
||||
// get middle point
|
||||
Vector2D middle = DRAGGINGTARGET->position().middle();
|
||||
|
||||
// and check its monitor
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromVector(middle);
|
||||
|
||||
if (PMONITOR && PMONITOR->m_activeWorkspace && DRAGGINGTARGET->floating() /* If we're resaizing a tiled target, don't do this */) {
|
||||
const auto WS = PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace;
|
||||
DRAGGINGTARGET->assignToSpace(WS->m_space);
|
||||
}
|
||||
|
||||
DRAGGINGTARGET->damageEntire();
|
||||
}
|
||||
54
src/layout/supplementary/DragController.hpp
Normal file
54
src/layout/supplementary/DragController.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "../target/Target.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
|
||||
namespace Layout {
|
||||
enum eRectCorner : uint8_t;
|
||||
}
|
||||
|
||||
namespace Layout::Supplementary {
|
||||
|
||||
// DragStateController contains logic to begin and end a drag, which shouldn't be part of the layout's job. It's stuff like
|
||||
// toggling float when dragging tiled, remembering sizes, checking deltas, etc.
|
||||
class CDragStateController {
|
||||
public:
|
||||
CDragStateController() = default;
|
||||
~CDragStateController() = default;
|
||||
|
||||
void dragBegin(SP<ITarget> target, eMouseBindMode mode);
|
||||
void dragEnd();
|
||||
|
||||
void mouseMove(const Vector2D& mousePos);
|
||||
eMouseBindMode mode() const;
|
||||
bool wasDraggingWindow() const;
|
||||
bool dragThresholdReached() const;
|
||||
void resetDragThresholdReached();
|
||||
bool draggingTiled() const;
|
||||
|
||||
/*
|
||||
Called to try to pick up window for dragging.
|
||||
Updates drag related variables and floats window if threshold reached.
|
||||
Return true to reject
|
||||
*/
|
||||
bool updateDragWindow();
|
||||
|
||||
SP<ITarget> target() const;
|
||||
|
||||
private:
|
||||
WP<ITarget> m_target;
|
||||
|
||||
eMouseBindMode m_dragMode = MBIND_INVALID;
|
||||
bool m_wasDraggingWindow = false;
|
||||
bool m_dragThresholdReached = false;
|
||||
bool m_draggingTiled = false;
|
||||
|
||||
int m_mouseMoveEventCount = 0;
|
||||
Vector2D m_beginDragXY;
|
||||
Vector2D m_lastDragXY;
|
||||
Vector2D m_beginDragPositionXY;
|
||||
Vector2D m_beginDragSizeXY;
|
||||
Vector2D m_draggingWindowOriginalFloatSize;
|
||||
Layout::eRectCorner m_grabbedCorner = sc<Layout::eRectCorner>(0) /* CORNER_NONE */;
|
||||
};
|
||||
};
|
||||
139
src/layout/supplementary/WorkspaceAlgoMatcher.cpp
Normal file
139
src/layout/supplementary/WorkspaceAlgoMatcher.cpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#include "WorkspaceAlgoMatcher.hpp"
|
||||
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
|
||||
#include "../algorithm/floating/default/DefaultFloatingAlgorithm.hpp"
|
||||
#include "../algorithm/tiled/dwindle/DwindleAlgorithm.hpp"
|
||||
#include "../algorithm/tiled/master/MasterAlgorithm.hpp"
|
||||
#include "../algorithm/tiled/scrolling/ScrollingAlgorithm.hpp"
|
||||
#include "../algorithm/tiled/monocle/MonocleAlgorithm.hpp"
|
||||
|
||||
#include "../../Compositor.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Layout::Supplementary;
|
||||
|
||||
constexpr const char* DEFAULT_FLOATING_ALGO = "default";
|
||||
constexpr const char* DEFAULT_TILED_ALGO = "dwindle";
|
||||
|
||||
const UP<CWorkspaceAlgoMatcher>& Supplementary::algoMatcher() {
|
||||
static UP<CWorkspaceAlgoMatcher> m = makeUnique<CWorkspaceAlgoMatcher>();
|
||||
return m;
|
||||
}
|
||||
|
||||
CWorkspaceAlgoMatcher::CWorkspaceAlgoMatcher() {
|
||||
m_tiledAlgos = {
|
||||
{"dwindle", [] { return makeUnique<Tiled::CDwindleAlgorithm>(); }},
|
||||
{"master", [] { return makeUnique<Tiled::CMasterAlgorithm>(); }},
|
||||
{"scrolling", [] { return makeUnique<Tiled::CScrollingAlgorithm>(); }},
|
||||
{"monocle", [] { return makeUnique<Tiled::CMonocleAlgorithm>(); }},
|
||||
};
|
||||
|
||||
m_floatingAlgos = {
|
||||
{"default", [] { return makeUnique<Floating::CDefaultFloatingAlgorithm>(); }},
|
||||
};
|
||||
|
||||
m_algoNames = {
|
||||
{&typeid(Tiled::CDwindleAlgorithm), "dwindle"},
|
||||
{&typeid(Tiled::CMasterAlgorithm), "master"},
|
||||
{&typeid(Tiled::CScrollingAlgorithm), "scrolling"},
|
||||
{&typeid(Tiled::CMonocleAlgorithm), "monocle"},
|
||||
{&typeid(Floating::CDefaultFloatingAlgorithm), "default"},
|
||||
};
|
||||
}
|
||||
|
||||
bool CWorkspaceAlgoMatcher::registerTiledAlgo(const std::string& name, const std::type_info* typeInfo, std::function<UP<ITiledAlgorithm>()>&& factory) {
|
||||
if (m_tiledAlgos.contains(name) || m_floatingAlgos.contains(name))
|
||||
return false;
|
||||
|
||||
m_tiledAlgos.emplace(name, std::move(factory));
|
||||
m_algoNames.emplace(typeInfo, name);
|
||||
|
||||
updateWorkspaceLayouts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWorkspaceAlgoMatcher::registerFloatingAlgo(const std::string& name, const std::type_info* typeInfo, std::function<UP<IFloatingAlgorithm>()>&& factory) {
|
||||
if (m_tiledAlgos.contains(name) || m_floatingAlgos.contains(name))
|
||||
return false;
|
||||
|
||||
m_floatingAlgos.emplace(name, std::move(factory));
|
||||
m_algoNames.emplace(typeInfo, name);
|
||||
|
||||
updateWorkspaceLayouts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWorkspaceAlgoMatcher::unregisterAlgo(const std::string& name) {
|
||||
if (!m_tiledAlgos.contains(name) && !m_floatingAlgos.contains(name))
|
||||
return false;
|
||||
|
||||
std::erase_if(m_algoNames, [&name](const auto& e) { return e.second == name; });
|
||||
|
||||
if (m_floatingAlgos.contains(name))
|
||||
m_floatingAlgos.erase(name);
|
||||
else
|
||||
m_tiledAlgos.erase(name);
|
||||
|
||||
// this is needed here to avoid situations where a plugin unloads and we still have a UP
|
||||
// to a plugin layout
|
||||
updateWorkspaceLayouts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UP<ITiledAlgorithm> CWorkspaceAlgoMatcher::algoForNameTiled(const std::string& s) {
|
||||
if (m_tiledAlgos.contains(s))
|
||||
return m_tiledAlgos.at(s)();
|
||||
return m_tiledAlgos.at(DEFAULT_TILED_ALGO)();
|
||||
}
|
||||
|
||||
UP<IFloatingAlgorithm> CWorkspaceAlgoMatcher::algoForNameFloat(const std::string& s) {
|
||||
if (m_floatingAlgos.contains(s))
|
||||
return m_floatingAlgos.at(s)();
|
||||
return m_floatingAlgos.at(DEFAULT_FLOATING_ALGO)();
|
||||
}
|
||||
|
||||
std::string CWorkspaceAlgoMatcher::tiledAlgoForWorkspace(const PHLWORKSPACE& w) {
|
||||
static auto PLAYOUT = CConfigValue<Hyprlang::STRING>("general:layout");
|
||||
|
||||
auto rule = g_pConfigManager->getWorkspaceRuleFor(w);
|
||||
return rule.layout.value_or(*PLAYOUT);
|
||||
}
|
||||
|
||||
SP<CAlgorithm> CWorkspaceAlgoMatcher::createAlgorithmForWorkspace(PHLWORKSPACE w) {
|
||||
return CAlgorithm::create(algoForNameTiled(tiledAlgoForWorkspace(w)), makeUnique<Floating::CDefaultFloatingAlgorithm>(), w->m_space);
|
||||
}
|
||||
|
||||
void CWorkspaceAlgoMatcher::updateWorkspaceLayouts() {
|
||||
// TODO: make this ID-based, string comparison is slow
|
||||
for (const auto& ws : g_pCompositor->getWorkspaces()) {
|
||||
if (!ws)
|
||||
continue;
|
||||
|
||||
const auto& TILED_ALGO = ws->m_space->algorithm()->tiledAlgo();
|
||||
|
||||
if (!TILED_ALGO)
|
||||
continue;
|
||||
|
||||
const auto LAYOUT_TO_USE = tiledAlgoForWorkspace(ws.lock());
|
||||
|
||||
if (m_algoNames.contains(&typeid(*TILED_ALGO.get())) && m_algoNames.at(&typeid(*TILED_ALGO.get())) == LAYOUT_TO_USE)
|
||||
continue;
|
||||
|
||||
// needs a switchup
|
||||
ws->m_space->algorithm()->updateTiledAlgo(algoForNameTiled(LAYOUT_TO_USE));
|
||||
}
|
||||
}
|
||||
|
||||
std::string CWorkspaceAlgoMatcher::getNameForTiledAlgo(const std::type_info* type) {
|
||||
if (m_algoNames.contains(type))
|
||||
return m_algoNames.at(type);
|
||||
return "unknown";
|
||||
}
|
||||
46
src/layout/supplementary/WorkspaceAlgoMatcher.hpp
Normal file
46
src/layout/supplementary/WorkspaceAlgoMatcher.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../desktop/DesktopTypes.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace Layout {
|
||||
class CAlgorithm;
|
||||
class ITiledAlgorithm;
|
||||
class IFloatingAlgorithm;
|
||||
}
|
||||
|
||||
namespace Layout::Supplementary {
|
||||
class CWorkspaceAlgoMatcher {
|
||||
public:
|
||||
CWorkspaceAlgoMatcher();
|
||||
~CWorkspaceAlgoMatcher() = default;
|
||||
|
||||
SP<CAlgorithm> createAlgorithmForWorkspace(PHLWORKSPACE w);
|
||||
void updateWorkspaceLayouts();
|
||||
std::string getNameForTiledAlgo(const std::type_info* type);
|
||||
|
||||
// these fns can fail due to name collisions
|
||||
bool registerTiledAlgo(const std::string& name, const std::type_info* typeInfo, std::function<UP<ITiledAlgorithm>()>&& factory);
|
||||
bool registerFloatingAlgo(const std::string& name, const std::type_info* typeInfo, std::function<UP<IFloatingAlgorithm>()>&& factory);
|
||||
|
||||
// this fn fails if the algo isn't registered
|
||||
bool unregisterAlgo(const std::string& name);
|
||||
|
||||
private:
|
||||
UP<ITiledAlgorithm> algoForNameTiled(const std::string& s);
|
||||
UP<IFloatingAlgorithm> algoForNameFloat(const std::string& s);
|
||||
|
||||
std::string tiledAlgoForWorkspace(const PHLWORKSPACE&);
|
||||
|
||||
std::map<std::string, std::function<UP<ITiledAlgorithm>()>> m_tiledAlgos;
|
||||
std::map<std::string, std::function<UP<IFloatingAlgorithm>()>> m_floatingAlgos;
|
||||
|
||||
std::map<const std::type_info*, std::string> m_algoNames;
|
||||
};
|
||||
|
||||
const UP<CWorkspaceAlgoMatcher>& algoMatcher();
|
||||
}
|
||||
146
src/layout/target/Target.cpp
Normal file
146
src/layout/target/Target.cpp
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include "Target.hpp"
|
||||
#include "../space/Space.hpp"
|
||||
#include "../../debug/log/Logger.hpp"
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
||||
using namespace Layout;
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
void ITarget::setPositionGlobal(const CBox& box) {
|
||||
m_box = box;
|
||||
m_box.round();
|
||||
}
|
||||
|
||||
void ITarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
if (m_space == space && !m_ghostSpace)
|
||||
return;
|
||||
|
||||
const bool HAD_SPACE = !!m_space;
|
||||
|
||||
if (m_space && !m_ghostSpace)
|
||||
m_space->remove(m_self.lock());
|
||||
|
||||
m_space = space;
|
||||
|
||||
if (space && HAD_SPACE)
|
||||
space->move(m_self.lock(), focalPoint);
|
||||
else if (space)
|
||||
space->add(m_self.lock());
|
||||
|
||||
if (!space)
|
||||
Log::logger->log(Log::WARN, "ITarget: assignToSpace with a null space?");
|
||||
|
||||
m_ghostSpace = false;
|
||||
|
||||
onUpdateSpace();
|
||||
}
|
||||
|
||||
void ITarget::setSpaceGhost(const SP<CSpace>& space) {
|
||||
if (m_space)
|
||||
assignToSpace(nullptr);
|
||||
|
||||
m_space = space;
|
||||
|
||||
m_ghostSpace = true;
|
||||
}
|
||||
|
||||
SP<CSpace> ITarget::space() const {
|
||||
return m_space;
|
||||
}
|
||||
|
||||
PHLWORKSPACE ITarget::workspace() const {
|
||||
if (!m_space)
|
||||
return nullptr;
|
||||
|
||||
return m_space->workspace();
|
||||
}
|
||||
|
||||
CBox ITarget::position() const {
|
||||
return m_box;
|
||||
}
|
||||
|
||||
void ITarget::rememberFloatingSize(const Vector2D& size) {
|
||||
m_floatingSize = size;
|
||||
}
|
||||
|
||||
Vector2D ITarget::lastFloatingSize() const {
|
||||
return m_floatingSize;
|
||||
}
|
||||
|
||||
void ITarget::recalc() {
|
||||
setPositionGlobal(m_box);
|
||||
}
|
||||
|
||||
void ITarget::setPseudo(bool x) {
|
||||
if (m_pseudo == x)
|
||||
return;
|
||||
|
||||
m_pseudo = x;
|
||||
|
||||
recalc();
|
||||
}
|
||||
|
||||
bool ITarget::isPseudo() const {
|
||||
return m_pseudo;
|
||||
}
|
||||
|
||||
void ITarget::setPseudoSize(const Vector2D& size) {
|
||||
m_pseudoSize = size;
|
||||
|
||||
recalc();
|
||||
}
|
||||
|
||||
Vector2D ITarget::pseudoSize() {
|
||||
return m_pseudoSize;
|
||||
}
|
||||
|
||||
void ITarget::swap(SP<ITarget> b) {
|
||||
const auto IS_FLOATING = floating();
|
||||
const auto IS_FLOATING_B = b->floating();
|
||||
|
||||
// Keep workspaces alive during a swap: moving one window will unref the ws
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
const auto PWS1 = workspace();
|
||||
// NOLINTNEXTLINE
|
||||
const auto PWS2 = b->workspace();
|
||||
|
||||
CScopeGuard x([&] {
|
||||
b->setFloating(IS_FLOATING);
|
||||
setFloating(IS_FLOATING_B);
|
||||
|
||||
// update the spaces
|
||||
b->onUpdateSpace();
|
||||
onUpdateSpace();
|
||||
});
|
||||
|
||||
if (b->space() == m_space) {
|
||||
// simplest
|
||||
m_space->swap(m_self.lock(), b);
|
||||
m_space->recalculate();
|
||||
return;
|
||||
}
|
||||
|
||||
// spaces differ
|
||||
if (m_space)
|
||||
m_space->swap(m_self.lock(), b);
|
||||
if (b->space())
|
||||
b->space()->swap(b, m_self.lock());
|
||||
|
||||
std::swap(m_space, b->m_space);
|
||||
|
||||
// recalc both
|
||||
if (m_space)
|
||||
m_space->recalculate();
|
||||
if (b->space())
|
||||
b->space()->recalculate();
|
||||
}
|
||||
|
||||
bool ITarget::wasTiling() const {
|
||||
return m_wasTiling;
|
||||
}
|
||||
|
||||
void ITarget::setWasTiling(bool x) {
|
||||
m_wasTiling = x;
|
||||
}
|
||||
79
src/layout/target/Target.hpp
Normal file
79
src/layout/target/Target.hpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
#include "../../desktop/Workspace.hpp"
|
||||
|
||||
#include <expected>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Layout {
|
||||
enum eTargetType : uint8_t {
|
||||
TARGET_TYPE_WINDOW = 0,
|
||||
TARGET_TYPE_GROUP,
|
||||
};
|
||||
|
||||
enum eGeometryFailure : uint8_t {
|
||||
GEOMETRY_NO_DESIRED = 0,
|
||||
GEOMETRY_INVALID_DESIRED = 1,
|
||||
};
|
||||
|
||||
class CSpace;
|
||||
|
||||
struct SGeometryRequested {
|
||||
Vector2D size;
|
||||
std::optional<Vector2D> pos;
|
||||
};
|
||||
|
||||
class ITarget {
|
||||
public:
|
||||
virtual ~ITarget() = default;
|
||||
|
||||
virtual eTargetType type() = 0;
|
||||
|
||||
// position is within its space
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual CBox position() const;
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual void setSpaceGhost(const SP<CSpace>& space);
|
||||
virtual SP<CSpace> space() const;
|
||||
virtual PHLWORKSPACE workspace() const;
|
||||
virtual PHLWINDOW window() const = 0;
|
||||
virtual void recalc();
|
||||
virtual bool wasTiling() const;
|
||||
virtual void setWasTiling(bool x);
|
||||
|
||||
virtual void rememberFloatingSize(const Vector2D& size);
|
||||
virtual Vector2D lastFloatingSize() const;
|
||||
|
||||
virtual void setPseudo(bool x);
|
||||
virtual bool isPseudo() const;
|
||||
virtual void setPseudoSize(const Vector2D& size);
|
||||
virtual Vector2D pseudoSize();
|
||||
virtual void swap(SP<ITarget> b);
|
||||
|
||||
//
|
||||
virtual bool floating() = 0;
|
||||
virtual void setFloating(bool x) = 0;
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry() = 0;
|
||||
virtual eFullscreenMode fullscreenMode() = 0;
|
||||
virtual void setFullscreenMode(eFullscreenMode mode) = 0;
|
||||
virtual std::optional<Vector2D> minSize() = 0;
|
||||
virtual std::optional<Vector2D> maxSize() = 0;
|
||||
virtual void damageEntire() = 0;
|
||||
virtual void warpPositionSize() = 0;
|
||||
virtual void onUpdateSpace() = 0;
|
||||
|
||||
protected:
|
||||
ITarget() = default;
|
||||
|
||||
CBox m_box;
|
||||
SP<CSpace> m_space;
|
||||
WP<ITarget> m_self;
|
||||
Vector2D m_floatingSize;
|
||||
bool m_pseudo = false;
|
||||
bool m_ghostSpace = false; // ghost space means a target belongs to a space, but isn't sent to the layout
|
||||
Vector2D m_pseudoSize = {1280, 720};
|
||||
bool m_wasTiling = false;
|
||||
};
|
||||
};
|
||||
92
src/layout/target/WindowGroupTarget.cpp
Normal file
92
src/layout/target/WindowGroupTarget.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#include "WindowGroupTarget.hpp"
|
||||
|
||||
#include "../space/Space.hpp"
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
#include "WindowTarget.hpp"
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<CWindowGroupTarget> CWindowGroupTarget::create(SP<Desktop::View::CGroup> g) {
|
||||
auto target = SP<CWindowGroupTarget>(new CWindowGroupTarget(g));
|
||||
target->m_self = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
CWindowGroupTarget::CWindowGroupTarget(SP<Desktop::View::CGroup> g) : m_group(g) {
|
||||
;
|
||||
}
|
||||
|
||||
eTargetType CWindowGroupTarget::type() {
|
||||
return TARGET_TYPE_GROUP;
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setPositionGlobal(const CBox& box) {
|
||||
ITarget::setPositionGlobal(box);
|
||||
|
||||
updatePos();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::updatePos() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->setPositionGlobal(m_box);
|
||||
}
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
|
||||
m_group->updateWorkspace(space->workspace());
|
||||
}
|
||||
|
||||
bool CWindowGroupTarget::floating() {
|
||||
return m_group->current()->m_target->floating();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setFloating(bool x) {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->setFloating(x);
|
||||
}
|
||||
}
|
||||
|
||||
std::expected<SGeometryRequested, eGeometryFailure> CWindowGroupTarget::desiredGeometry() {
|
||||
return m_group->current()->m_target->desiredGeometry();
|
||||
}
|
||||
|
||||
PHLWINDOW CWindowGroupTarget::window() const {
|
||||
return m_group->current();
|
||||
}
|
||||
|
||||
eFullscreenMode CWindowGroupTarget::fullscreenMode() {
|
||||
return m_group->current()->m_fullscreenState.internal;
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::setFullscreenMode(eFullscreenMode mode) {
|
||||
m_group->current()->m_fullscreenState.internal = mode;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowGroupTarget::minSize() {
|
||||
return m_group->current()->minSize();
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowGroupTarget::maxSize() {
|
||||
return m_group->current()->maxSize();
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::damageEntire() {
|
||||
g_pHyprRenderer->damageWindow(m_group->current());
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::warpPositionSize() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->warpPositionSize();
|
||||
}
|
||||
}
|
||||
|
||||
void CWindowGroupTarget::onUpdateSpace() {
|
||||
for (const auto& w : m_group->windows()) {
|
||||
w->m_target->onUpdateSpace();
|
||||
}
|
||||
}
|
||||
39
src/layout/target/WindowGroupTarget.hpp
Normal file
39
src/layout/target/WindowGroupTarget.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
#include "../../desktop/view/Group.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class CWindowGroupTarget : public ITarget {
|
||||
public:
|
||||
static SP<CWindowGroupTarget> create(SP<Desktop::View::CGroup> g);
|
||||
virtual ~CWindowGroupTarget() = default;
|
||||
|
||||
virtual eTargetType type();
|
||||
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual PHLWINDOW window() const;
|
||||
|
||||
virtual bool floating();
|
||||
virtual void setFloating(bool x);
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry();
|
||||
virtual eFullscreenMode fullscreenMode();
|
||||
virtual void setFullscreenMode(eFullscreenMode mode);
|
||||
virtual std::optional<Vector2D> minSize();
|
||||
virtual std::optional<Vector2D> maxSize();
|
||||
virtual void damageEntire();
|
||||
virtual void warpPositionSize();
|
||||
virtual void onUpdateSpace();
|
||||
|
||||
private:
|
||||
CWindowGroupTarget(SP<Desktop::View::CGroup> g);
|
||||
|
||||
void updatePos();
|
||||
|
||||
WP<Desktop::View::CGroup> m_group;
|
||||
};
|
||||
};
|
||||
363
src/layout/target/WindowTarget.cpp
Normal file
363
src/layout/target/WindowTarget.cpp
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
#include "WindowTarget.hpp"
|
||||
|
||||
#include "../space/Space.hpp"
|
||||
#include "../algorithm/Algorithm.hpp"
|
||||
|
||||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
#include "../../xwayland/XSurface.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
using namespace Layout;
|
||||
|
||||
SP<ITarget> CWindowTarget::create(PHLWINDOW w) {
|
||||
auto target = SP<CWindowTarget>(new CWindowTarget(w));
|
||||
target->m_self = target;
|
||||
return target;
|
||||
}
|
||||
|
||||
CWindowTarget::CWindowTarget(PHLWINDOW w) : m_window(w) {
|
||||
;
|
||||
}
|
||||
|
||||
eTargetType CWindowTarget::type() {
|
||||
return TARGET_TYPE_WINDOW;
|
||||
}
|
||||
|
||||
void CWindowTarget::setPositionGlobal(const CBox& box) {
|
||||
ITarget::setPositionGlobal(box);
|
||||
|
||||
updatePos();
|
||||
}
|
||||
|
||||
void CWindowTarget::updatePos() {
|
||||
|
||||
if (!m_space)
|
||||
return;
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
return;
|
||||
|
||||
if (floating() && fullscreenMode() != FSMODE_MAXIMIZED) {
|
||||
m_window->m_position = m_box.pos();
|
||||
m_window->m_size = m_box.size();
|
||||
|
||||
*m_window->m_realPosition = m_box.pos();
|
||||
*m_window->m_realSize = m_box.size();
|
||||
|
||||
m_window->sendWindowSize();
|
||||
m_window->updateWindowDecos();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Tiled is more complicated.
|
||||
|
||||
const auto PMONITOR = m_space->workspace()->m_monitor;
|
||||
const auto PWORKSPACE = m_space->workspace();
|
||||
|
||||
// for gaps outer
|
||||
const auto MONITOR_WORKAREA = m_space->workArea();
|
||||
const bool DISPLAYLEFT = STICKS(m_box.x, MONITOR_WORKAREA.x);
|
||||
const bool DISPLAYRIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w);
|
||||
const bool DISPLAYTOP = STICKS(m_box.y, MONITOR_WORKAREA.y);
|
||||
const bool DISPLAYBOTTOM = STICKS(m_box.y + m_box.h, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h);
|
||||
|
||||
// this is used for scrolling, so that the gaps are correct when a window is the full width and has neighbors
|
||||
const bool DISPLAYINVERSELEFT = STICKS(m_box.x, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w);
|
||||
const bool DISPLAYINVERSERIGHT = STICKS(m_box.x + m_box.w, MONITOR_WORKAREA.x);
|
||||
|
||||
// get specific gaps and rules for this workspace,
|
||||
// if user specified them in config
|
||||
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(PWORKSPACE);
|
||||
|
||||
if (!validMapped(m_window)) {
|
||||
if (m_window)
|
||||
g_layoutManager->removeTarget(m_window->layoutTarget());
|
||||
return;
|
||||
}
|
||||
|
||||
if (fullscreenMode() == FSMODE_FULLSCREEN)
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->damageWindow(window());
|
||||
|
||||
static auto PGAPSINDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_in");
|
||||
auto* const PGAPSIN = sc<CCssGapData*>((PGAPSINDATA.ptr())->getData());
|
||||
|
||||
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
|
||||
CBox nodeBox = m_box;
|
||||
nodeBox.round();
|
||||
|
||||
m_window->m_size = nodeBox.size();
|
||||
m_window->m_position = nodeBox.pos();
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
|
||||
auto calcPos = m_window->m_position;
|
||||
auto calcSize = m_window->m_size;
|
||||
|
||||
const static auto REQUESTEDRATIO = CConfigValue<Hyprlang::VEC2>("layout:single_window_aspect_ratio");
|
||||
const static auto REQUESTEDRATIOTOLERANCE = CConfigValue<Hyprlang::FLOAT>("layout:single_window_aspect_ratio_tolerance");
|
||||
|
||||
Vector2D ratioPadding;
|
||||
|
||||
if ((*REQUESTEDRATIO).y != 0 && m_space->algorithm()->tiledTargets() <= 1) {
|
||||
const Vector2D originalSize = MONITOR_WORKAREA.size();
|
||||
|
||||
const double requestedRatio = (*REQUESTEDRATIO).x / (*REQUESTEDRATIO).y;
|
||||
const double originalRatio = originalSize.x / originalSize.y;
|
||||
|
||||
if (requestedRatio > originalRatio) {
|
||||
double padding = originalSize.y - (originalSize.x / requestedRatio);
|
||||
|
||||
if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.y)
|
||||
ratioPadding = Vector2D{0., padding};
|
||||
} else if (requestedRatio < originalRatio) {
|
||||
double padding = originalSize.x - (originalSize.y * requestedRatio);
|
||||
|
||||
if (padding / 2 > (*REQUESTEDRATIOTOLERANCE) * originalSize.x)
|
||||
ratioPadding = Vector2D{padding, 0.};
|
||||
}
|
||||
}
|
||||
|
||||
const auto GAPOFFSETTOPLEFT = Vector2D(sc<double>(DISPLAYLEFT ? 0 : (DISPLAYINVERSELEFT ? 2 * gapsIn.m_left : gapsIn.m_left)), sc<double>(DISPLAYTOP ? 0 : gapsIn.m_top));
|
||||
|
||||
const auto GAPOFFSETBOTTOMRIGHT =
|
||||
Vector2D(sc<double>(DISPLAYRIGHT ? 0 : (DISPLAYINVERSERIGHT ? 2 * gapsIn.m_right : gapsIn.m_right)), sc<double>(DISPLAYBOTTOM ? 0 : gapsIn.m_bottom));
|
||||
|
||||
calcPos = calcPos + GAPOFFSETTOPLEFT + ratioPadding / 2;
|
||||
calcSize = calcSize - GAPOFFSETTOPLEFT - GAPOFFSETBOTTOMRIGHT - ratioPadding;
|
||||
|
||||
if (isPseudo()) {
|
||||
// Calculate pseudo
|
||||
float scale = 1;
|
||||
|
||||
// adjust if doesn't fit
|
||||
if (m_pseudoSize.x > calcSize.x || m_pseudoSize.y > calcSize.y) {
|
||||
if (m_pseudoSize.x > calcSize.x)
|
||||
scale = calcSize.x / m_pseudoSize.x;
|
||||
|
||||
if (m_pseudoSize.y * scale > calcSize.y)
|
||||
scale = calcSize.y / m_pseudoSize.y;
|
||||
|
||||
auto DELTA = calcSize - m_pseudoSize * scale;
|
||||
calcSize = m_pseudoSize * scale;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
} else {
|
||||
auto DELTA = calcSize - m_pseudoSize;
|
||||
calcPos = calcPos + DELTA / 2.f; // center
|
||||
calcSize = m_pseudoSize;
|
||||
}
|
||||
}
|
||||
|
||||
const auto RESERVED = m_window->getFullWindowReservedArea();
|
||||
calcPos = calcPos + RESERVED.topLeft;
|
||||
calcSize = calcSize - (RESERVED.topLeft + RESERVED.bottomRight);
|
||||
|
||||
Vector2D availableSpace = calcSize;
|
||||
|
||||
static auto PCLAMP_TILED = CConfigValue<Hyprlang::INT>("misc:size_limits_tiled");
|
||||
|
||||
if (*PCLAMP_TILED) {
|
||||
const auto borderSize = m_window->getRealBorderSize();
|
||||
Vector2D monitorAvailable = MONITOR_WORKAREA.size() - Vector2D{2.0 * borderSize, 2.0 * borderSize};
|
||||
|
||||
Vector2D minSize = m_window->m_ruleApplicator->minSize().valueOr(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||
Vector2D maxSize = m_window->isFullscreen() ? Vector2D{INFINITY, INFINITY} :
|
||||
m_window->m_ruleApplicator->maxSize().valueOr(Vector2D{INFINITY, INFINITY}).clamp(Vector2D{0, 0}, monitorAvailable);
|
||||
calcSize = calcSize.clamp(minSize, maxSize);
|
||||
|
||||
calcPos += (availableSpace - calcSize) / 2.0;
|
||||
|
||||
calcPos.x = std::clamp(calcPos.x, MONITOR_WORKAREA.x + borderSize, MONITOR_WORKAREA.x + MONITOR_WORKAREA.w - calcSize.x - borderSize);
|
||||
calcPos.y = std::clamp(calcPos.y, MONITOR_WORKAREA.y + borderSize, MONITOR_WORKAREA.y + MONITOR_WORKAREA.h - calcSize.y - borderSize);
|
||||
}
|
||||
|
||||
if (m_window->onSpecialWorkspace() && !m_window->isFullscreen()) {
|
||||
// if special, we adjust the coords a bit
|
||||
static auto PSCALEFACTOR = CConfigValue<Hyprlang::FLOAT>("dwindle:special_scale_factor");
|
||||
|
||||
CBox wb = {calcPos + (calcSize - calcSize * *PSCALEFACTOR) / 2.f, calcSize * *PSCALEFACTOR};
|
||||
wb.round(); // avoid rounding mess
|
||||
|
||||
*m_window->m_realPosition = wb.pos();
|
||||
*m_window->m_realSize = wb.size();
|
||||
} else {
|
||||
CBox wb = {calcPos, calcSize};
|
||||
wb.round(); // avoid rounding mess
|
||||
|
||||
*m_window->m_realSize = wb.size();
|
||||
*m_window->m_realPosition = wb.pos();
|
||||
}
|
||||
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindowTarget::assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint) {
|
||||
if (!space) {
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
return;
|
||||
}
|
||||
|
||||
// keep the ref here so that moveToWorkspace doesn't unref the workspace
|
||||
// and assignToSpace doesn't think this is a new target because space wp is dead
|
||||
const auto WSREF = space->workspace();
|
||||
|
||||
m_window->m_monitor = space->workspace()->m_monitor;
|
||||
m_window->moveToWorkspace(space->workspace());
|
||||
|
||||
// layout and various update fns want the target to already have m_workspace set
|
||||
ITarget::assignToSpace(space, focalPoint);
|
||||
|
||||
m_window->updateToplevel();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
bool CWindowTarget::floating() {
|
||||
return m_window->m_isFloating;
|
||||
}
|
||||
|
||||
void CWindowTarget::setFloating(bool x) {
|
||||
if (x == m_window->m_isFloating)
|
||||
return;
|
||||
|
||||
m_window->m_isFloating = x;
|
||||
m_window->m_pinned = false;
|
||||
|
||||
m_window->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FLOATING);
|
||||
}
|
||||
|
||||
Vector2D CWindowTarget::clampSizeForDesired(const Vector2D& size) const {
|
||||
Vector2D newSize = size;
|
||||
if (const auto m = m_window->minSize(); m)
|
||||
newSize = newSize.clamp(*m);
|
||||
if (const auto m = m_window->maxSize(); m)
|
||||
newSize = newSize.clamp(Vector2D{MIN_WINDOW_SIZE, MIN_WINDOW_SIZE}, *m);
|
||||
return newSize;
|
||||
}
|
||||
|
||||
std::expected<SGeometryRequested, eGeometryFailure> CWindowTarget::desiredGeometry() {
|
||||
|
||||
SGeometryRequested requested;
|
||||
|
||||
CBox DESIRED_GEOM = g_pXWaylandManager->getGeometryForWindow(m_window.lock());
|
||||
const auto PMONITOR = m_window->m_monitor.lock();
|
||||
|
||||
requested.size = clampSizeForDesired(DESIRED_GEOM.size());
|
||||
|
||||
if (m_window->m_isX11) {
|
||||
Vector2D xy = {DESIRED_GEOM.x, DESIRED_GEOM.y};
|
||||
xy = g_pXWaylandManager->xwaylandToWaylandCoords(xy);
|
||||
requested.pos = xy;
|
||||
}
|
||||
|
||||
const auto STOREDSIZE = m_window->m_ruleApplicator->persistentSize().valueOrDefault() ? g_pConfigManager->getStoredFloatingSize(m_window.lock()) : std::nullopt;
|
||||
|
||||
if (STOREDSIZE)
|
||||
requested.size = clampSizeForDesired(*STOREDSIZE);
|
||||
|
||||
if (!PMONITOR) {
|
||||
Log::logger->log(Log::ERR, "{:m} has an invalid monitor in desiredGeometry!", m_window.lock());
|
||||
return std::unexpected(GEOMETRY_NO_DESIRED);
|
||||
}
|
||||
|
||||
if (DESIRED_GEOM.width <= 2 || DESIRED_GEOM.height <= 2) {
|
||||
const auto SURFACE = m_window->wlSurface()->resource();
|
||||
|
||||
if (SURFACE->m_current.size.x > 5 && SURFACE->m_current.size.y > 5) {
|
||||
// center on mon and call it a day
|
||||
requested.pos.reset();
|
||||
requested.size = clampSizeForDesired(SURFACE->m_current.size);
|
||||
return requested;
|
||||
}
|
||||
|
||||
if (m_window->m_isX11 && m_window->isX11OverrideRedirect()) {
|
||||
// check OR windows, they like their shit
|
||||
const auto SIZE = clampSizeForDesired(m_window->m_xwaylandSurface->m_geometry.w > 0 && m_window->m_xwaylandSurface->m_geometry.h > 0 ?
|
||||
m_window->m_xwaylandSurface->m_geometry.size() :
|
||||
Vector2D{600, 400});
|
||||
|
||||
if (m_window->m_xwaylandSurface->m_geometry.x != 0 && m_window->m_xwaylandSurface->m_geometry.y != 0) {
|
||||
requested.size = SIZE;
|
||||
requested.pos = g_pXWaylandManager->xwaylandToWaylandCoords(m_window->m_xwaylandSurface->m_geometry.pos());
|
||||
return requested;
|
||||
}
|
||||
}
|
||||
|
||||
return std::unexpected(m_window->m_isX11 && m_window->isX11OverrideRedirect() ? GEOMETRY_INVALID_DESIRED : GEOMETRY_NO_DESIRED);
|
||||
}
|
||||
|
||||
// TODO: detect a popup in a more consistent way.
|
||||
if ((DESIRED_GEOM.x == 0 && DESIRED_GEOM.y == 0) || !m_window->m_isX11) {
|
||||
// middle of parent if available
|
||||
if (!m_window->m_isX11) {
|
||||
if (const auto PARENT = m_window->parent(); PARENT) {
|
||||
const auto POS = PARENT->m_realPosition->goal() + PARENT->m_realSize->goal() / 2.F - DESIRED_GEOM.size() / 2.F;
|
||||
requested.pos = POS;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if it is, we respect where it wants to put itself, but apply monitor offset if outside
|
||||
// most of these are popups
|
||||
|
||||
Vector2D pos;
|
||||
|
||||
if (const auto POPENMON = g_pCompositor->getMonitorFromVector(DESIRED_GEOM.middle()); POPENMON->m_id != PMONITOR->m_id)
|
||||
pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y) - POPENMON->m_position + PMONITOR->m_position;
|
||||
else
|
||||
pos = Vector2D(DESIRED_GEOM.x, DESIRED_GEOM.y);
|
||||
|
||||
requested.pos = pos;
|
||||
}
|
||||
|
||||
if (DESIRED_GEOM.w <= 2 || DESIRED_GEOM.h <= 2)
|
||||
return std::unexpected(GEOMETRY_NO_DESIRED);
|
||||
|
||||
return requested;
|
||||
}
|
||||
|
||||
PHLWINDOW CWindowTarget::window() const {
|
||||
return m_window.lock();
|
||||
}
|
||||
|
||||
eFullscreenMode CWindowTarget::fullscreenMode() {
|
||||
return m_window->m_fullscreenState.internal;
|
||||
}
|
||||
|
||||
void CWindowTarget::setFullscreenMode(eFullscreenMode mode) {
|
||||
if (floating() && m_window->m_fullscreenState.internal == FSMODE_NONE)
|
||||
rememberFloatingSize(m_box.size());
|
||||
|
||||
m_window->m_fullscreenState.internal = mode;
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowTarget::minSize() {
|
||||
return m_window->minSize();
|
||||
}
|
||||
|
||||
std::optional<Vector2D> CWindowTarget::maxSize() {
|
||||
return m_window->maxSize();
|
||||
}
|
||||
|
||||
void CWindowTarget::damageEntire() {
|
||||
g_pHyprRenderer->damageWindow(m_window.lock());
|
||||
}
|
||||
|
||||
void CWindowTarget::warpPositionSize() {
|
||||
m_window->m_realSize->warp();
|
||||
m_window->m_realPosition->warp();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
|
||||
void CWindowTarget::onUpdateSpace() {
|
||||
if (!space())
|
||||
return;
|
||||
|
||||
m_window->m_monitor = space()->workspace()->m_monitor;
|
||||
m_window->moveToWorkspace(space()->workspace());
|
||||
m_window->updateToplevel();
|
||||
m_window->updateWindowDecos();
|
||||
}
|
||||
40
src/layout/target/WindowTarget.hpp
Normal file
40
src/layout/target/WindowTarget.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "Target.hpp"
|
||||
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
|
||||
namespace Layout {
|
||||
|
||||
class CWindowTarget : public ITarget {
|
||||
public:
|
||||
static SP<ITarget> create(PHLWINDOW w);
|
||||
virtual ~CWindowTarget() = default;
|
||||
|
||||
virtual eTargetType type();
|
||||
|
||||
virtual void setPositionGlobal(const CBox& box);
|
||||
virtual void assignToSpace(const SP<CSpace>& space, std::optional<Vector2D> focalPoint = std::nullopt);
|
||||
virtual PHLWINDOW window() const;
|
||||
|
||||
virtual bool floating();
|
||||
virtual void setFloating(bool x);
|
||||
virtual std::expected<SGeometryRequested, eGeometryFailure> desiredGeometry();
|
||||
virtual eFullscreenMode fullscreenMode();
|
||||
virtual void setFullscreenMode(eFullscreenMode mode);
|
||||
virtual std::optional<Vector2D> minSize();
|
||||
virtual std::optional<Vector2D> maxSize();
|
||||
virtual void damageEntire();
|
||||
virtual void warpPositionSize();
|
||||
virtual void onUpdateSpace();
|
||||
|
||||
private:
|
||||
CWindowTarget(PHLWINDOW w);
|
||||
|
||||
Vector2D clampSizeForDesired(const Vector2D& size) const;
|
||||
|
||||
void updatePos();
|
||||
|
||||
PHLWINDOWREF m_window;
|
||||
};
|
||||
};
|
||||
|
|
@ -19,13 +19,19 @@
|
|||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../managers/EventManager.hpp"
|
||||
#include "../render/Renderer.hpp"
|
||||
#include "../hyprerror/HyprError.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../desktop/rule/windowRule/WindowRule.hpp"
|
||||
#include "../desktop/rule/Engine.hpp"
|
||||
#include "../desktop/view/Group.hpp"
|
||||
#include "../layout/LayoutManager.hpp"
|
||||
#include "../layout/target/WindowTarget.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../layout/algorithm/Algorithm.hpp"
|
||||
#include "../layout/algorithm/tiled/master/MasterAlgorithm.hpp"
|
||||
#include "../layout/algorithm/tiled/monocle/MonocleAlgorithm.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <iterator>
|
||||
|
|
@ -322,10 +328,10 @@ void CKeybindManager::updateXKBTranslationState() {
|
|||
}
|
||||
|
||||
bool CKeybindManager::ensureMouseBindState() {
|
||||
if (!g_pInputManager->m_currentlyDraggedWindow)
|
||||
if (!g_layoutManager->dragController()->target())
|
||||
return false;
|
||||
|
||||
if (!g_pInputManager->m_currentlyDraggedWindow.expired()) {
|
||||
if (g_layoutManager->dragController()->target()) {
|
||||
changeMouseBindMode(MBIND_INVALID);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -368,7 +374,7 @@ bool CKeybindManager::tryMoveFocusToMonitor(PHLMONITOR monitor) {
|
|||
const auto PNEWWINDOW = PNEWWORKSPACE->getLastFocusedWindow();
|
||||
if (PNEWWINDOW) {
|
||||
updateRelativeCursorCoords();
|
||||
Desktop::focusState()->fullWindowFocus(PNEWWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PNEWWINDOW, Desktop::FOCUS_REASON_KEYBIND);
|
||||
PNEWWINDOW->warpCursor();
|
||||
|
||||
if (*PNOWARPS == 0 || *PFOLLOWMOUSE < 2) {
|
||||
|
|
@ -377,7 +383,7 @@ bool CKeybindManager::tryMoveFocusToMonitor(PHLMONITOR monitor) {
|
|||
g_pInputManager->m_forcedFocus.reset();
|
||||
}
|
||||
} else {
|
||||
Desktop::focusState()->rawWindowFocus(nullptr);
|
||||
Desktop::focusState()->rawWindowFocus(nullptr, Desktop::FOCUS_REASON_KEYBIND);
|
||||
g_pCompositor->warpCursorTo(monitor->middle());
|
||||
}
|
||||
Desktop::focusState()->rawMonitorFocus(monitor);
|
||||
|
|
@ -398,10 +404,10 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO, bool forceFSCy
|
|||
g_pInputManager->unconstrainMouse();
|
||||
|
||||
if (PLASTWINDOW && PLASTWINDOW->m_workspace == PWINDOWTOCHANGETO->m_workspace && PLASTWINDOW->isFullscreen())
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, nullptr, forceFSCycle);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle);
|
||||
else {
|
||||
updateRelativeCursorCoords();
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, nullptr, forceFSCycle);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWTOCHANGETO, Desktop::FOCUS_REASON_KEYBIND, nullptr, forceFSCycle);
|
||||
PWINDOWTOCHANGETO->warpCursor();
|
||||
|
||||
// Move mouse focus to the new window if required by current follow_mouse and warp modes
|
||||
|
|
@ -751,9 +757,9 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
// Require mouse to stay inside drag_threshold for clicks, outside for drags
|
||||
// Check if either a mouse bind has triggered or currently over the threshold (maybe there is no mouse bind on the same key)
|
||||
const auto THRESHOLDREACHED = key.mousePosAtPress.distanceSq(g_pInputManager->getMouseCoordsInternal()) > std::pow(*PDRAGTHRESHOLD, 2);
|
||||
if (k->click && (g_pInputManager->m_dragThresholdReached || THRESHOLDREACHED))
|
||||
if (k->click && (g_layoutManager->dragController()->dragThresholdReached() || THRESHOLDREACHED))
|
||||
continue;
|
||||
else if (k->drag && !g_pInputManager->m_dragThresholdReached && !THRESHOLDREACHED)
|
||||
else if (k->drag && !g_layoutManager->dragController()->dragThresholdReached() && !THRESHOLDREACHED)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -810,7 +816,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
found = true;
|
||||
}
|
||||
|
||||
g_pInputManager->m_dragThresholdReached = false;
|
||||
g_layoutManager->dragController()->resetDragThresholdReached();
|
||||
|
||||
// if keybind wasn't found (or dispatcher said to) then pass event
|
||||
res.passEvent |= !found;
|
||||
|
|
@ -1112,32 +1118,16 @@ static SDispatchResult toggleActiveFloatingCore(std::string args, std::optional<
|
|||
return {};
|
||||
|
||||
// remove drag status
|
||||
if (!g_pInputManager->m_currentlyDraggedWindow.expired())
|
||||
if (g_layoutManager->dragController()->target())
|
||||
CKeybindManager::changeMouseBindMode(MBIND_INVALID);
|
||||
|
||||
if (PWINDOW->m_groupData.pNextWindow.lock() && PWINDOW->m_groupData.pNextWindow.lock() != PWINDOW) {
|
||||
const auto PCURRENT = PWINDOW->getGroupCurrent();
|
||||
|
||||
PCURRENT->m_isFloating = !PCURRENT->m_isFloating;
|
||||
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(PCURRENT);
|
||||
|
||||
PHLWINDOW curr = PCURRENT->m_groupData.pNextWindow.lock();
|
||||
while (curr != PCURRENT) {
|
||||
curr->m_isFloating = PCURRENT->m_isFloating;
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
}
|
||||
} else {
|
||||
PWINDOW->m_isFloating = !PWINDOW->m_isFloating;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(PWINDOW);
|
||||
}
|
||||
g_layoutManager->changeFloatingMode(PWINDOW->layoutTarget());
|
||||
|
||||
if (PWINDOW->m_workspace) {
|
||||
PWINDOW->m_workspace->updateWindows();
|
||||
PWINDOW->m_workspace->updateWindowData();
|
||||
}
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWINDOW->monitorID());
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
return {};
|
||||
|
|
@ -1163,8 +1153,7 @@ SDispatchResult CKeybindManager::centerWindow(std::string args) {
|
|||
|
||||
const auto PMONITOR = PWINDOW->m_monitor.lock();
|
||||
|
||||
*PWINDOW->m_realPosition = PMONITOR->logicalBoxMinusReserved().middle() - PWINDOW->m_realSize->goal() / 2.f;
|
||||
PWINDOW->m_position = PWINDOW->m_realPosition->goal();
|
||||
PWINDOW->layoutTarget()->setPositionGlobal(CBox{PMONITOR->logicalBoxMinusReserved().middle() - PWINDOW->m_realSize->goal() / 2.F, PWINDOW->layoutTarget()->position().size()});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1180,10 +1169,7 @@ SDispatchResult CKeybindManager::toggleActivePseudo(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return {.success = false, .error = "Window not found"};
|
||||
|
||||
PWINDOW->m_isPseudotiled = !PWINDOW->m_isPseudotiled;
|
||||
|
||||
if (!PWINDOW->isFullscreen())
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(PWINDOW);
|
||||
PWINDOW->layoutTarget()->setPseudo(!PWINDOW->layoutTarget()->isPseudo());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1276,7 +1262,7 @@ SDispatchResult CKeybindManager::changeworkspace(std::string args) {
|
|||
if (PMONITOR != PMONITORWORKSPACEOWNER) {
|
||||
Vector2D middle = PMONITORWORKSPACEOWNER->middle();
|
||||
if (const auto PLAST = pWorkspaceToChangeTo->getLastFocusedWindow(); PLAST) {
|
||||
Desktop::focusState()->fullWindowFocus(PLAST);
|
||||
Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_KEYBIND);
|
||||
if (*PWORKSPACECENTERON == 1)
|
||||
middle = PLAST->middle();
|
||||
}
|
||||
|
|
@ -1421,7 +1407,7 @@ SDispatchResult CKeybindManager::moveActiveToWorkspace(std::string args) {
|
|||
|
||||
pMonitor->changeWorkspace(pWorkspace);
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND);
|
||||
PWINDOW->warpCursor();
|
||||
|
||||
return {};
|
||||
|
|
@ -1465,7 +1451,7 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
|
|||
if (const auto PATCOORDS =
|
||||
g_pCompositor->vectorToWindowUnified(OLDMIDDLE, Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING, PWINDOW);
|
||||
PATCOORDS)
|
||||
Desktop::focusState()->fullWindowFocus(PATCOORDS);
|
||||
Desktop::focusState()->fullWindowFocus(PATCOORDS, Desktop::FOCUS_REASON_KEYBIND);
|
||||
else
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
|
@ -1474,38 +1460,35 @@ SDispatchResult CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
|
|||
}
|
||||
|
||||
SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
||||
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
|
||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
||||
static auto PGROUPCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_groupfirst");
|
||||
char arg = args[0];
|
||||
static auto PFULLCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_fullscreen");
|
||||
static auto PGROUPCYCLE = CConfigValue<Hyprlang::INT>("binds:movefocus_cycles_groupfirst");
|
||||
Math::eDirection dir = Math::fromChar(args[0]);
|
||||
|
||||
if (!isDirection(args)) {
|
||||
Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg);
|
||||
return {.success = false, .error = std::format("Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)};
|
||||
if (dir == Math::DIRECTION_DEFAULT) {
|
||||
Log::logger->log(Log::ERR, "Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
||||
return {.success = false, .error = std::format("Cannot move focus in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])};
|
||||
}
|
||||
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
if (!PLASTWINDOW || !PLASTWINDOW->aliveAndVisible()) {
|
||||
if (*PMONITORFALLBACK)
|
||||
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg));
|
||||
|
||||
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir));
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto PWINDOWTOCHANGETO = *PFULLCYCLE && PLASTWINDOW->isFullscreen() ?
|
||||
g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, arg != 'd' && arg != 'b' && arg != 'r') :
|
||||
g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
|
||||
g_pCompositor->getWindowCycle(PLASTWINDOW, true, {}, false, dir != Math::DIRECTION_DOWN && dir != Math::DIRECTION_RIGHT) :
|
||||
g_pCompositor->getWindowInDirection(PLASTWINDOW, dir);
|
||||
|
||||
// Prioritize focus change within groups if the window is a part of it.
|
||||
if (*PGROUPCYCLE && PLASTWINDOW->m_groupData.pNextWindow) {
|
||||
if (*PGROUPCYCLE && PLASTWINDOW->m_group) {
|
||||
auto isTheOnlyGroupOnWs = !PWINDOWTOCHANGETO && g_pCompositor->m_monitors.size() == 1;
|
||||
if (arg == 'l' && (PLASTWINDOW != PLASTWINDOW->getGroupHead() || isTheOnlyGroupOnWs)) {
|
||||
PLASTWINDOW->setGroupCurrent(PLASTWINDOW->getGroupPrevious());
|
||||
if (dir == Math::DIRECTION_LEFT && (PLASTWINDOW != PLASTWINDOW->m_group->head() || isTheOnlyGroupOnWs)) {
|
||||
PLASTWINDOW->m_group->moveCurrent(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
else if (arg == 'r' && (PLASTWINDOW != PLASTWINDOW->getGroupTail() || isTheOnlyGroupOnWs)) {
|
||||
PLASTWINDOW->setGroupCurrent(PLASTWINDOW->m_groupData.pNextWindow.lock());
|
||||
else if (dir == Math::DIRECTION_RIGHT && (PLASTWINDOW != PLASTWINDOW->m_group->tail() || isTheOnlyGroupOnWs)) {
|
||||
PLASTWINDOW->m_group->moveCurrent(true);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
|
@ -1516,52 +1499,51 @@ SDispatchResult CKeybindManager::moveFocusTo(std::string args) {
|
|||
return {};
|
||||
}
|
||||
|
||||
Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", arg);
|
||||
Log::logger->log(Log::DEBUG, "No window found in direction {}, looking for a monitor", Math::toString(dir));
|
||||
|
||||
if (*PMONITORFALLBACK && tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)))
|
||||
if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(dir)))
|
||||
return {};
|
||||
|
||||
static auto PNOFALLBACK = CConfigValue<Hyprlang::INT>("general:no_focus_fallback");
|
||||
if (*PNOFALLBACK)
|
||||
return {.success = false, .error = std::format("Nothing to focus to in direction {}", arg)};
|
||||
return {.success = false, .error = std::format("Nothing to focus to in direction {}", Math::toString(dir))};
|
||||
|
||||
Log::logger->log(Log::DEBUG, "No monitor found in direction {}, getting the inverse edge", arg);
|
||||
Log::logger->log(Log::DEBUG, "No monitor found in direction {}, getting the inverse edge", Math::toString(dir));
|
||||
|
||||
const auto PMONITOR = PLASTWINDOW->m_monitor.lock();
|
||||
|
||||
if (!PMONITOR)
|
||||
return {.success = false, .error = "last window has no monitor?"};
|
||||
|
||||
if (arg == 'l' || arg == 'r') {
|
||||
if (dir == Math::DIRECTION_LEFT || dir == Math::DIRECTION_RIGHT) {
|
||||
if (STICKS(PLASTWINDOW->m_position.x, PMONITOR->m_position.x) && STICKS(PLASTWINDOW->m_size.x, PMONITOR->m_size.x))
|
||||
return {.success = false, .error = "move does not make sense, would return back"};
|
||||
} else if (STICKS(PLASTWINDOW->m_position.y, PMONITOR->m_position.y) && STICKS(PLASTWINDOW->m_size.y, PMONITOR->m_size.y))
|
||||
return {.success = false, .error = "move does not make sense, would return back"};
|
||||
|
||||
CBox box = PMONITOR->logicalBox();
|
||||
switch (arg) {
|
||||
case 'l':
|
||||
switch (dir) {
|
||||
case Math::DIRECTION_LEFT:
|
||||
box.x += box.w;
|
||||
box.w = 1;
|
||||
break;
|
||||
case 'r':
|
||||
case Math::DIRECTION_RIGHT:
|
||||
box.x -= 1;
|
||||
box.w = 1;
|
||||
break;
|
||||
case 'u':
|
||||
case 't':
|
||||
case Math::DIRECTION_UP:
|
||||
box.y += box.h;
|
||||
box.h = 1;
|
||||
break;
|
||||
case 'd':
|
||||
case 'b':
|
||||
case Math::DIRECTION_DOWN:
|
||||
box.y -= 1;
|
||||
box.h = 1;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
const auto PWINDOWCANDIDATE = g_pCompositor->getWindowInDirection(box, PMONITOR->m_activeSpecialWorkspace ? PMONITOR->m_activeSpecialWorkspace : PMONITOR->m_activeWorkspace,
|
||||
arg, PLASTWINDOW, PLASTWINDOW->m_isFloating);
|
||||
dir, PLASTWINDOW, PLASTWINDOW->m_isFloating);
|
||||
if (PWINDOWCANDIDATE)
|
||||
switchToWindow(PWINDOWCANDIDATE);
|
||||
|
||||
|
|
@ -1598,7 +1580,6 @@ SDispatchResult CKeybindManager::focusCurrentOrLast(std::string args) {
|
|||
}
|
||||
|
||||
SDispatchResult CKeybindManager::swapActive(std::string args) {
|
||||
char arg = args[0];
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
PHLWINDOW PWINDOWTOCHANGETO = nullptr;
|
||||
|
||||
|
|
@ -1608,9 +1589,10 @@ SDispatchResult CKeybindManager::swapActive(std::string args) {
|
|||
if (PLASTWINDOW->isFullscreen())
|
||||
return {.success = false, .error = "Can't swap fullscreen window"};
|
||||
|
||||
if (isDirection(args))
|
||||
PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
|
||||
else
|
||||
if (isDirection(args)) {
|
||||
Math::eDirection dir = Math::fromChar(args[0]);
|
||||
PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, dir);
|
||||
} else
|
||||
PWINDOWTOCHANGETO = g_pCompositor->getWindowByRegex(args);
|
||||
|
||||
if (!PWINDOWTOCHANGETO || PWINDOWTOCHANGETO == PLASTWINDOW) {
|
||||
|
|
@ -1621,13 +1603,12 @@ SDispatchResult CKeybindManager::swapActive(std::string args) {
|
|||
Log::logger->log(Log::DEBUG, "Swapping active window with {}", args);
|
||||
|
||||
updateRelativeCursorCoords();
|
||||
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO);
|
||||
g_layoutManager->switchTargets(PLASTWINDOW->layoutTarget(), PWINDOWTOCHANGETO->layoutTarget(), true);
|
||||
PLASTWINDOW->warpCursor();
|
||||
return {};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::moveActiveTo(std::string args) {
|
||||
char arg = args[0];
|
||||
bool silent = args.ends_with(" silent");
|
||||
if (silent)
|
||||
args = args.substr(0, args.length() - 7);
|
||||
|
|
@ -1645,9 +1626,10 @@ SDispatchResult CKeybindManager::moveActiveTo(std::string args) {
|
|||
return {};
|
||||
}
|
||||
|
||||
if (!isDirection(args)) {
|
||||
Log::logger->log(Log::ERR, "Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg);
|
||||
return {.success = false, .error = std::format("Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)};
|
||||
Math::eDirection dir = Math::fromChar(args[0]);
|
||||
if (dir == Math::DIRECTION_DEFAULT) {
|
||||
Log::logger->log(Log::ERR, "Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
||||
return {.success = false, .error = std::format("Cannot move window in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])};
|
||||
}
|
||||
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
|
|
@ -1658,59 +1640,11 @@ SDispatchResult CKeybindManager::moveActiveTo(std::string args) {
|
|||
if (PLASTWINDOW->isFullscreen())
|
||||
return {.success = false, .error = "Can't move fullscreen window"};
|
||||
|
||||
if (PLASTWINDOW->m_isFloating) {
|
||||
std::optional<float> vPosx, vPosy;
|
||||
const auto PMONITOR = PLASTWINDOW->m_monitor.lock();
|
||||
const auto BORDERSIZE = PLASTWINDOW->getRealBorderSize();
|
||||
static auto PGAPSCUSTOMDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:float_gaps");
|
||||
static auto PGAPSOUTDATA = CConfigValue<Hyprlang::CUSTOMTYPE>("general:gaps_out");
|
||||
auto* PGAPSOUT = sc<CCssGapData*>(PGAPSCUSTOMDATA.ptr()->getData());
|
||||
if (PGAPSOUT->m_left < 0 || PGAPSOUT->m_right < 0 || PGAPSOUT->m_top < 0 || PGAPSOUT->m_bottom < 0)
|
||||
PGAPSOUT = sc<CCssGapData*>(PGAPSOUTDATA.ptr()->getData());
|
||||
updateRelativeCursorCoords();
|
||||
|
||||
switch (arg) {
|
||||
case 'l': vPosx = PMONITOR->m_reservedArea.left() + BORDERSIZE + PMONITOR->m_position.x + PGAPSOUT->m_left; break;
|
||||
case 'r':
|
||||
vPosx = PMONITOR->m_size.x - PMONITOR->m_reservedArea.right() - PLASTWINDOW->m_realSize->goal().x - BORDERSIZE + PMONITOR->m_position.x - PGAPSOUT->m_right;
|
||||
break;
|
||||
case 't':
|
||||
case 'u': vPosy = PMONITOR->m_reservedArea.top() + BORDERSIZE + PMONITOR->m_position.y + PGAPSOUT->m_top; break;
|
||||
case 'b':
|
||||
case 'd':
|
||||
vPosy = PMONITOR->m_size.y - PMONITOR->m_reservedArea.bottom() - PLASTWINDOW->m_realSize->goal().y - BORDERSIZE + PMONITOR->m_position.y - PGAPSOUT->m_bottom;
|
||||
break;
|
||||
}
|
||||
|
||||
*PLASTWINDOW->m_realPosition = Vector2D(vPosx.value_or(PLASTWINDOW->m_realPosition->goal().x), vPosy.value_or(PLASTWINDOW->m_realPosition->goal().y));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// If the window to change to is on the same workspace, switch them
|
||||
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
|
||||
if (PWINDOWTOCHANGETO) {
|
||||
updateRelativeCursorCoords();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(PLASTWINDOW, args, silent);
|
||||
if (!silent)
|
||||
PLASTWINDOW->warpCursor();
|
||||
return {};
|
||||
}
|
||||
|
||||
static auto PMONITORFALLBACK = CConfigValue<Hyprlang::INT>("binds:window_direction_monitor_fallback");
|
||||
if (!*PMONITORFALLBACK)
|
||||
return {};
|
||||
|
||||
// Otherwise, we always want to move to the next monitor in that direction
|
||||
const auto PMONITORTOCHANGETO = g_pCompositor->getMonitorInDirection(arg);
|
||||
if (!PMONITORTOCHANGETO)
|
||||
return {.success = false, .error = "Nowhere to move active window to"};
|
||||
|
||||
const auto PWORKSPACE = PMONITORTOCHANGETO->m_activeWorkspace;
|
||||
if (silent)
|
||||
moveActiveToWorkspaceSilent(PWORKSPACE->getConfigName());
|
||||
else
|
||||
moveActiveToWorkspace(PWORKSPACE->getConfigName());
|
||||
g_layoutManager->moveInDirection(PLASTWINDOW->layoutTarget(), args, silent);
|
||||
if (!silent)
|
||||
PLASTWINDOW->warpCursor();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1724,10 +1658,10 @@ SDispatchResult CKeybindManager::toggleGroup(std::string args) {
|
|||
if (PWINDOW->isFullscreen())
|
||||
g_pCompositor->setWindowFullscreenInternal(PWINDOW, FSMODE_NONE);
|
||||
|
||||
if (PWINDOW->m_groupData.pNextWindow.expired())
|
||||
PWINDOW->createGroup();
|
||||
if (!PWINDOW->m_group)
|
||||
PWINDOW->m_group = Desktop::View::CGroup::create({PWINDOW});
|
||||
else
|
||||
PWINDOW->destroyGroup();
|
||||
PWINDOW->m_group->destroy();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -1738,89 +1672,40 @@ SDispatchResult CKeybindManager::changeGroupActive(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return {.success = false, .error = "Window not found"};
|
||||
|
||||
if (PWINDOW->m_groupData.pNextWindow.expired())
|
||||
return {.success = false, .error = "No next window in group"};
|
||||
if (!PWINDOW->m_group)
|
||||
return {.success = false, .error = "No group"};
|
||||
|
||||
if (PWINDOW->m_groupData.pNextWindow.lock() == PWINDOW)
|
||||
if (PWINDOW->m_group->size() == 1)
|
||||
return {.success = false, .error = "Only one window in group"};
|
||||
|
||||
if (isNumber(args, false)) {
|
||||
// index starts from '1'; '0' means last window
|
||||
const int INDEX = std::stoi(args);
|
||||
if (INDEX > PWINDOW->getGroupSize())
|
||||
return {.success = false, .error = "Index too big, there aren't that many windows in this group"};
|
||||
if (INDEX == 0)
|
||||
PWINDOW->setGroupCurrent(PWINDOW->getGroupTail());
|
||||
else
|
||||
PWINDOW->setGroupCurrent(PWINDOW->getGroupWindowByIndex(INDEX - 1));
|
||||
try {
|
||||
const int INDEX = std::stoi(args);
|
||||
PWINDOW->m_group->setCurrent(INDEX);
|
||||
} catch (...) { return {.success = false, .error = "invalid idx"}; }
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (args != "b" && args != "prev")
|
||||
PWINDOW->setGroupCurrent(PWINDOW->m_groupData.pNextWindow.lock());
|
||||
PWINDOW->m_group->moveCurrent(true);
|
||||
else
|
||||
PWINDOW->setGroupCurrent(PWINDOW->getGroupPrevious());
|
||||
PWINDOW->m_group->moveCurrent(false);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::toggleSplit(std::string args) {
|
||||
SLayoutMessageHeader header;
|
||||
header.pWindow = Desktop::focusState()->window();
|
||||
|
||||
if (!header.pWindow)
|
||||
return {.success = false, .error = "Window not found"};
|
||||
|
||||
const auto PWORKSPACE = header.pWindow->m_workspace;
|
||||
|
||||
if (PWORKSPACE->m_hasFullscreenWindow)
|
||||
return {.success = false, .error = "Can't split windows that already split"};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "togglesplit");
|
||||
|
||||
return {};
|
||||
return {.success = false, .error = "removed - use layoutmsg"};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::swapSplit(std::string args) {
|
||||
SLayoutMessageHeader header;
|
||||
header.pWindow = Desktop::focusState()->window();
|
||||
|
||||
if (!header.pWindow)
|
||||
return {.success = false, .error = "Window not found"};
|
||||
|
||||
const auto PWORKSPACE = header.pWindow->m_workspace;
|
||||
|
||||
if (PWORKSPACE->m_hasFullscreenWindow)
|
||||
return {.success = false, .error = "Can't split windows that already split"};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->layoutMessage(header, "swapsplit");
|
||||
|
||||
return {};
|
||||
return {.success = false, .error = "removed - use layoutmsg"};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::alterSplitRatio(std::string args) {
|
||||
std::optional<float> splitResult;
|
||||
bool exact = false;
|
||||
|
||||
if (args.starts_with("exact")) {
|
||||
exact = true;
|
||||
splitResult = getPlusMinusKeywordResult(args.substr(5), 0);
|
||||
} else
|
||||
splitResult = getPlusMinusKeywordResult(args, 0);
|
||||
|
||||
if (!splitResult.has_value()) {
|
||||
Log::logger->log(Log::ERR, "Splitratio invalid in alterSplitRatio!");
|
||||
return {.success = false, .error = "Splitratio invalid in alterSplitRatio!"};
|
||||
}
|
||||
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
|
||||
if (!PLASTWINDOW)
|
||||
return {.success = false, .error = "Window not found"};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->alterSplitRatio(PLASTWINDOW, splitResult.value(), exact);
|
||||
|
||||
return {};
|
||||
return {.success = false, .error = "removed - use layoutmsg"};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::focusMonitor(std::string arg) {
|
||||
|
|
@ -1903,58 +1788,7 @@ SDispatchResult CKeybindManager::moveCursor(std::string args) {
|
|||
}
|
||||
|
||||
SDispatchResult CKeybindManager::workspaceOpt(std::string args) {
|
||||
|
||||
// current workspace
|
||||
const auto PWORKSPACE = Desktop::focusState()->monitor()->m_activeWorkspace;
|
||||
|
||||
if (!PWORKSPACE)
|
||||
return {.success = false, .error = "Workspace not found"}; // ????
|
||||
|
||||
if (args == "allpseudo") {
|
||||
PWORKSPACE->m_defaultPseudo = !PWORKSPACE->m_defaultPseudo;
|
||||
|
||||
// apply
|
||||
for (auto const& w : g_pCompositor->m_windows) {
|
||||
if (!w->m_isMapped || w->m_workspace != PWORKSPACE)
|
||||
continue;
|
||||
|
||||
w->m_isPseudotiled = PWORKSPACE->m_defaultPseudo;
|
||||
}
|
||||
} else if (args == "allfloat") {
|
||||
PWORKSPACE->m_defaultFloating = !PWORKSPACE->m_defaultFloating;
|
||||
// apply
|
||||
|
||||
// we make a copy because changeWindowFloatingMode might invalidate the iterator
|
||||
std::vector<PHLWINDOW> ptrs(g_pCompositor->m_windows.begin(), g_pCompositor->m_windows.end());
|
||||
|
||||
for (auto const& w : ptrs) {
|
||||
if (!w->m_isMapped || w->m_workspace != PWORKSPACE || w->isHidden())
|
||||
continue;
|
||||
|
||||
if (!w->m_requestsFloat && w->m_isFloating != PWORKSPACE->m_defaultFloating) {
|
||||
const auto SAVEDPOS = w->m_realPosition->goal();
|
||||
const auto SAVEDSIZE = w->m_realSize->goal();
|
||||
|
||||
w->m_isFloating = PWORKSPACE->m_defaultFloating;
|
||||
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(w);
|
||||
|
||||
if (PWORKSPACE->m_defaultFloating) {
|
||||
w->m_realPosition->setValueAndWarp(SAVEDPOS);
|
||||
w->m_realSize->setValueAndWarp(SAVEDSIZE);
|
||||
*w->m_realSize = w->m_realSize->value() + Vector2D(4, 4);
|
||||
*w->m_realPosition = w->m_realPosition->value() - Vector2D(2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log::logger->log(Log::ERR, "Invalid arg in workspaceOpt, opt \"{}\" doesn't exist.", args);
|
||||
return {.success = false, .error = std::format("Invalid arg in workspaceOpt, opt \"{}\" doesn't exist.", args)};
|
||||
}
|
||||
|
||||
// recalc mon
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(Desktop::focusState()->monitor()->m_id);
|
||||
|
||||
return {};
|
||||
return {.success = false, .error = "workspaceopt is deprecated"};
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::renameWorkspace(std::string args) {
|
||||
|
|
@ -2183,7 +2017,7 @@ SDispatchResult CKeybindManager::resizeActive(std::string args) {
|
|||
if (SIZ.x < 1 || SIZ.y < 1)
|
||||
return {.success = false, .error = "Invalid size provided"};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PLASTWINDOW->m_realSize->goal());
|
||||
g_layoutManager->resizeTarget(SIZ - PLASTWINDOW->m_realSize->goal(), PLASTWINDOW->layoutTarget());
|
||||
|
||||
if (PLASTWINDOW->m_realSize->goal().x > 1 && PLASTWINDOW->m_realSize->goal().y > 1)
|
||||
PLASTWINDOW->setHidden(false);
|
||||
|
|
@ -2202,7 +2036,7 @@ SDispatchResult CKeybindManager::moveActive(std::string args) {
|
|||
|
||||
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(args, PLASTWINDOW->m_realPosition->goal());
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->moveActiveWindow(POS - PLASTWINDOW->m_realPosition->goal());
|
||||
g_layoutManager->moveTarget(POS - PLASTWINDOW->m_realPosition->goal(), PLASTWINDOW->layoutTarget());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -2224,7 +2058,7 @@ SDispatchResult CKeybindManager::moveWindow(std::string args) {
|
|||
|
||||
const auto POS = g_pCompositor->parseWindowVectorArgsRelative(MOVECMD, PWINDOW->m_realPosition->goal());
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->moveActiveWindow(POS - PWINDOW->m_realPosition->goal(), PWINDOW);
|
||||
g_layoutManager->moveTarget(POS - PWINDOW->m_realPosition->goal(), PWINDOW->layoutTarget());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -2249,7 +2083,7 @@ SDispatchResult CKeybindManager::resizeWindow(std::string args) {
|
|||
if (SIZ.x < 1 || SIZ.y < 1)
|
||||
return {.success = false, .error = "Invalid size provided"};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow(SIZ - PWINDOW->m_realSize->goal(), CORNER_NONE, PWINDOW);
|
||||
g_layoutManager->resizeTarget(SIZ - PWINDOW->m_realSize->goal(), PWINDOW->layoutTarget(), Layout::CORNER_NONE);
|
||||
|
||||
if (PWINDOW->m_realSize->goal().x > 1 && PWINDOW->m_realSize->goal().y > 1)
|
||||
PWINDOW->setHidden(false);
|
||||
|
|
@ -2271,14 +2105,32 @@ SDispatchResult CKeybindManager::circleNext(std::string arg) {
|
|||
|
||||
CVarList args{arg, 0, 's', true};
|
||||
|
||||
const auto PREV = args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l");
|
||||
|
||||
std::optional<bool> floatStatus = {};
|
||||
if (args.contains("tile") || args.contains("tiled"))
|
||||
floatStatus = false;
|
||||
else if (args.contains("float") || args.contains("floating"))
|
||||
if (args.contains("tile") || args.contains("tiled")) {
|
||||
// if we want just tiled, and we are on a tiled window, use layoutmsg for layouts that support it
|
||||
|
||||
if (!Desktop::focusState()->window()->m_isFloating) {
|
||||
if (const auto SPACE = Desktop::focusState()->window()->layoutTarget()->space(); SPACE) {
|
||||
|
||||
constexpr const std::array<const std::type_info*, 2> LAYOUTS_WITH_CYCLE_NEXT = {
|
||||
&typeid(Layout::Tiled::CMonocleAlgorithm),
|
||||
&typeid(Layout::Tiled::CMasterAlgorithm),
|
||||
};
|
||||
|
||||
if (std::ranges::contains(LAYOUTS_WITH_CYCLE_NEXT, &typeid(*SPACE->algorithm()->tiledAlgo().get()))) {
|
||||
CKeybindManager::layoutmsg(PREV ? "cyclenext, b" : "cyclenext");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.contains("float") || args.contains("floating"))
|
||||
floatStatus = true;
|
||||
|
||||
const auto VISIBLE = args.contains("visible") || args.contains("v");
|
||||
const auto PREV = args.contains("prev") || args.contains("p") || args.contains("last") || args.contains("l");
|
||||
const auto NEXT = args.contains("next") || args.contains("n"); // prev is default in classic alt+tab
|
||||
const auto HIST = args.contains("hist") || args.contains("h");
|
||||
const auto& w = HIST ? g_pCompositor->getWindowCycleHist(Desktop::focusState()->window(), true, floatStatus, VISIBLE, NEXT) :
|
||||
|
|
@ -2311,7 +2163,7 @@ SDispatchResult CKeybindManager::focusWindow(std::string regexp) {
|
|||
changeworkspace(PWORKSPACE->getConfigName());
|
||||
}
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW, nullptr, false);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND, nullptr, false);
|
||||
|
||||
PWINDOW->warpCursor();
|
||||
|
||||
|
|
@ -2347,12 +2199,12 @@ SDispatchResult CKeybindManager::toggleSwallow(std::string args) {
|
|||
// Unswallow
|
||||
pWindow->m_swallowed->m_currentlySwallowed = false;
|
||||
pWindow->m_swallowed->setHidden(false);
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow->m_swallowed.lock());
|
||||
g_layoutManager->newTarget(pWindow->m_swallowed->layoutTarget(), pWindow->m_workspace->m_space);
|
||||
} else {
|
||||
// Reswallow
|
||||
pWindow->m_swallowed->m_currentlySwallowed = true;
|
||||
pWindow->m_swallowed->setHidden(true);
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow->m_swallowed.lock());
|
||||
g_layoutManager->removeTarget(pWindow->m_swallowed->layoutTarget());
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
@ -2613,9 +2465,9 @@ SDispatchResult CKeybindManager::sendshortcut(std::string args) {
|
|||
}
|
||||
|
||||
SDispatchResult CKeybindManager::layoutmsg(std::string msg) {
|
||||
SLayoutMessageHeader hd = {Desktop::focusState()->window()};
|
||||
g_pLayoutManager->getCurrentLayout()->layoutMessage(hd, msg);
|
||||
|
||||
auto ret = g_layoutManager->layoutMsg(msg);
|
||||
if (!ret)
|
||||
return {.success = false, .error = ret.error()};
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -2669,11 +2521,11 @@ SDispatchResult CKeybindManager::swapnext(std::string arg) {
|
|||
if (toSwap == PLASTWINDOW)
|
||||
toSwap = g_pCompositor->getWindowCycle(PLASTWINDOW, true, std::nullopt, false, NEED_PREV);
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, toSwap);
|
||||
g_layoutManager->switchTargets(PLASTWINDOW->layoutTarget(), toSwap->layoutTarget(), false);
|
||||
|
||||
PLASTWINDOW->m_lastCycledWindow = toSwap;
|
||||
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_KEYBIND);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -2722,7 +2574,7 @@ SDispatchResult CKeybindManager::pinActive(std::string args) {
|
|||
return {.success = false, .error = "pin: window not found"};
|
||||
}
|
||||
|
||||
PWINDOW->m_workspace = PMONITOR->m_activeWorkspace;
|
||||
PWINDOW->moveToWorkspace(PMONITOR->m_activeWorkspace);
|
||||
|
||||
PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_PINNED);
|
||||
|
||||
|
|
@ -2734,6 +2586,8 @@ SDispatchResult CKeybindManager::pinActive(std::string args) {
|
|||
g_pEventManager->postEvent(SHyprIPCEvent{"pin", std::format("{:x},{}", rc<uintptr_t>(PWINDOW.get()), sc<int>(PWINDOW->m_pinned))});
|
||||
EMIT_HOOK_EVENT("pin", PWINDOW);
|
||||
|
||||
g_pHyprRenderer->damageWindow(PWINDOW, true);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
@ -2760,7 +2614,7 @@ SDispatchResult CKeybindManager::mouse(std::string args) {
|
|||
|
||||
SDispatchResult CKeybindManager::changeMouseBindMode(const eMouseBindMode MODE) {
|
||||
if (MODE != MBIND_INVALID) {
|
||||
if (!g_pInputManager->m_currentlyDraggedWindow.expired() || g_pInputManager->m_dragMode != MBIND_INVALID)
|
||||
if (g_layoutManager->dragController()->target())
|
||||
return {};
|
||||
|
||||
const auto MOUSECOORDS = g_pInputManager->getMouseCoordsInternal();
|
||||
|
|
@ -2769,21 +2623,17 @@ SDispatchResult CKeybindManager::changeMouseBindMode(const eMouseBindMode MODE)
|
|||
if (!PWINDOW)
|
||||
return SDispatchResult{.passEvent = true};
|
||||
|
||||
if (!PWINDOW->isFullscreen() && MODE == MBIND_MOVE)
|
||||
PWINDOW->checkInputOnDecos(INPUT_TYPE_DRAG_START, MOUSECOORDS);
|
||||
if (!PWINDOW->isFullscreen() && MODE == MBIND_MOVE) {
|
||||
if (PWINDOW->checkInputOnDecos(INPUT_TYPE_DRAG_START, MOUSECOORDS))
|
||||
return SDispatchResult{.passEvent = false};
|
||||
}
|
||||
|
||||
if (g_pInputManager->m_currentlyDraggedWindow.expired())
|
||||
g_pInputManager->m_currentlyDraggedWindow = PWINDOW;
|
||||
|
||||
g_pInputManager->m_dragMode = MODE;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onBeginDragWindow();
|
||||
g_layoutManager->beginDragTarget(PWINDOW->layoutTarget(), MODE);
|
||||
} else {
|
||||
if (g_pInputManager->m_currentlyDraggedWindow.expired() || g_pInputManager->m_dragMode == MBIND_INVALID)
|
||||
if (!g_layoutManager->dragController()->target())
|
||||
return {};
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onEndDragWindow();
|
||||
g_pInputManager->m_dragMode = MODE;
|
||||
g_layoutManager->endDragTarget();
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
@ -2845,17 +2695,15 @@ SDispatchResult CKeybindManager::lockActiveGroup(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return {.success = false, .error = "No window found"};
|
||||
|
||||
if (!PWINDOW->m_groupData.pNextWindow.lock())
|
||||
if (!PWINDOW->m_group)
|
||||
return {.success = false, .error = "Not a group"};
|
||||
|
||||
const auto PHEAD = PWINDOW->getGroupHead();
|
||||
|
||||
if (args == "lock")
|
||||
PHEAD->m_groupData.locked = true;
|
||||
PWINDOW->m_group->setLocked(true);
|
||||
else if (args == "toggle")
|
||||
PHEAD->m_groupData.locked = !PHEAD->m_groupData.locked;
|
||||
PWINDOW->m_group->setLocked(!PWINDOW->m_group->locked());
|
||||
else
|
||||
PHEAD->m_groupData.locked = false;
|
||||
PWINDOW->m_group->setLocked(false);
|
||||
|
||||
PWINDOW->updateDecorationValues();
|
||||
|
||||
|
|
@ -2863,25 +2711,21 @@ SDispatchResult CKeybindManager::lockActiveGroup(std::string args) {
|
|||
}
|
||||
|
||||
void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowInDirection) {
|
||||
if (pWindow->m_groupData.deny)
|
||||
if (!pWindowInDirection->m_group || pWindowInDirection->m_group->denied())
|
||||
return;
|
||||
|
||||
updateRelativeCursorCoords();
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes grouped property!
|
||||
|
||||
if (pWindow->m_monitor != pWindowInDirection->m_monitor) {
|
||||
pWindow->moveToWorkspace(pWindowInDirection->m_workspace);
|
||||
pWindow->m_monitor = pWindowInDirection->m_monitor;
|
||||
}
|
||||
|
||||
static auto USECURRPOS = CConfigValue<Hyprlang::INT>("group:insert_after_current");
|
||||
(*USECURRPOS ? pWindowInDirection : pWindowInDirection->getGroupTail())->insertWindowToGroup(pWindow);
|
||||
pWindowInDirection->m_group->add(pWindow);
|
||||
|
||||
pWindowInDirection->setGroupCurrent(pWindow);
|
||||
pWindowInDirection->m_group->setCurrent(pWindow);
|
||||
pWindow->updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
|
||||
Desktop::focusState()->fullWindowFocus(pWindow);
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||
pWindow->warpCursor();
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"moveintogroup", std::format("{:x}", rc<uintptr_t>(pWindow.get()))});
|
||||
|
|
@ -2889,70 +2733,51 @@ void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowIn
|
|||
|
||||
void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& dir) {
|
||||
static auto BFOCUSREMOVEDWINDOW = CConfigValue<Hyprlang::INT>("group:focus_removed_window");
|
||||
const auto PWINDOWPREV = pWindow->getGroupPrevious();
|
||||
eDirection direction;
|
||||
|
||||
switch (dir[0]) {
|
||||
case 't':
|
||||
case 'u': direction = DIRECTION_UP; break;
|
||||
case 'd':
|
||||
case 'b': direction = DIRECTION_DOWN; break;
|
||||
case 'l': direction = DIRECTION_LEFT; break;
|
||||
case 'r': direction = DIRECTION_RIGHT; break;
|
||||
default: direction = DIRECTION_DEFAULT;
|
||||
}
|
||||
if (!pWindow->m_group)
|
||||
return;
|
||||
|
||||
updateRelativeCursorCoords();
|
||||
WP<Desktop::View::CGroup> group = pWindow->m_group;
|
||||
|
||||
if (pWindow->m_groupData.pNextWindow.lock() == pWindow) {
|
||||
pWindow->destroyGroup();
|
||||
} else {
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow);
|
||||
pWindow->m_group->remove(pWindow);
|
||||
|
||||
const auto GROUPSLOCKEDPREV = g_pKeybindManager->m_groupsLocked;
|
||||
g_pKeybindManager->m_groupsLocked = true;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow, direction);
|
||||
|
||||
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
|
||||
}
|
||||
|
||||
if (*BFOCUSREMOVEDWINDOW) {
|
||||
Desktop::focusState()->fullWindowFocus(pWindow);
|
||||
if (*BFOCUSREMOVEDWINDOW || !group) {
|
||||
Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_KEYBIND);
|
||||
pWindow->warpCursor();
|
||||
} else {
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOWPREV);
|
||||
PWINDOWPREV->warpCursor();
|
||||
Desktop::focusState()->fullWindowFocus(group->current(), Desktop::FOCUS_REASON_KEYBIND);
|
||||
group->current()->warpCursor();
|
||||
}
|
||||
|
||||
g_pEventManager->postEvent(SHyprIPCEvent{"moveoutofgroup", std::format("{:x}", rc<uintptr_t>(pWindow.get()))});
|
||||
}
|
||||
|
||||
SDispatchResult CKeybindManager::moveIntoGroup(std::string args) {
|
||||
char arg = args[0];
|
||||
|
||||
static auto PIGNOREGROUPLOCK = CConfigValue<Hyprlang::INT>("binds:ignore_group_lock");
|
||||
|
||||
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked)
|
||||
return {};
|
||||
|
||||
if (!isDirection(args)) {
|
||||
Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg);
|
||||
return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)};
|
||||
Math::eDirection dir = Math::fromChar(args[0]);
|
||||
if (dir == Math::DIRECTION_DEFAULT) {
|
||||
Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
||||
return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])};
|
||||
}
|
||||
|
||||
const auto PWINDOW = Desktop::focusState()->window();
|
||||
|
||||
if (!PWINDOW || PWINDOW->m_groupData.deny)
|
||||
if (!PWINDOW)
|
||||
return {};
|
||||
|
||||
auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, arg);
|
||||
auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir);
|
||||
|
||||
if (!PWINDOWINDIR || !PWINDOWINDIR->m_groupData.pNextWindow.lock())
|
||||
if (!PWINDOWINDIR || !PWINDOWINDIR->m_group)
|
||||
return {};
|
||||
|
||||
const auto GROUP = PWINDOWINDIR->m_group;
|
||||
|
||||
// Do not move window into locked group if binds:ignore_group_lock is false
|
||||
if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_groupData.locked || (PWINDOW->m_groupData.pNextWindow.lock() && PWINDOW->getGroupHead()->m_groupData.locked)))
|
||||
if (!*PIGNOREGROUPLOCK && (GROUP->locked() || (PWINDOW->m_group && PWINDOW->m_group->locked())))
|
||||
return {};
|
||||
|
||||
moveWindowIntoGroup(PWINDOW, PWINDOWINDIR);
|
||||
|
|
@ -2976,7 +2801,7 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) {
|
|||
if (!PWINDOW)
|
||||
return {.success = false, .error = "No window found"};
|
||||
|
||||
if (!PWINDOW->m_groupData.pNextWindow.lock())
|
||||
if (!PWINDOW->m_group)
|
||||
return {.success = false, .error = "Window not in a group"};
|
||||
|
||||
moveWindowOutOfGroup(PWINDOW);
|
||||
|
|
@ -2985,13 +2810,12 @@ SDispatchResult CKeybindManager::moveOutOfGroup(std::string args) {
|
|||
}
|
||||
|
||||
SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) {
|
||||
char arg = args[0];
|
||||
static auto PIGNOREGROUPLOCK = CConfigValue<Hyprlang::INT>("binds:ignore_group_lock");
|
||||
|
||||
static auto PIGNOREGROUPLOCK = CConfigValue<Hyprlang::INT>("binds:ignore_group_lock");
|
||||
|
||||
if (!isDirection(args)) {
|
||||
Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg);
|
||||
return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", arg)};
|
||||
Math::eDirection dir = Math::fromChar(args[0]);
|
||||
if (dir == Math::DIRECTION_DEFAULT) {
|
||||
Log::logger->log(Log::ERR, "Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0]);
|
||||
return {.success = false, .error = std::format("Cannot move into group in direction {}, unsupported direction. Supported: l,r,u/t,d/b", args[0])};
|
||||
}
|
||||
|
||||
const auto PWINDOW = Desktop::focusState()->window();
|
||||
|
|
@ -3002,35 +2826,35 @@ SDispatchResult CKeybindManager::moveWindowOrGroup(std::string args) {
|
|||
return {};
|
||||
|
||||
if (!*PIGNOREGROUPLOCK && g_pKeybindManager->m_groupsLocked) {
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args);
|
||||
g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, arg);
|
||||
const auto PWINDOWINDIR = g_pCompositor->getWindowInDirection(PWINDOW, dir);
|
||||
|
||||
const bool ISWINDOWGROUP = PWINDOW->m_groupData.pNextWindow;
|
||||
const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->getGroupHead()->m_groupData.locked;
|
||||
const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_groupData.pNextWindow.lock() == PWINDOW;
|
||||
const bool ISWINDOWGROUP = PWINDOW->m_group;
|
||||
const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->m_group->locked();
|
||||
const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_group->size() == 1;
|
||||
|
||||
updateRelativeCursorCoords();
|
||||
|
||||
// note: PWINDOWINDIR is not null implies !PWINDOW->m_isFloating
|
||||
if (PWINDOWINDIR && PWINDOWINDIR->m_groupData.pNextWindow) { // target is group
|
||||
if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_groupData.locked || ISWINDOWGROUPLOCKED || PWINDOW->m_groupData.deny)) {
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args);
|
||||
if (PWINDOWINDIR && PWINDOWINDIR->m_group) { // target is group
|
||||
if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->m_group->locked() || ISWINDOWGROUPLOCKED || PWINDOW->m_group->denied())) {
|
||||
g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args);
|
||||
PWINDOW->warpCursor();
|
||||
} else
|
||||
moveWindowIntoGroup(PWINDOW, PWINDOWINDIR);
|
||||
} else if (PWINDOWINDIR) { // target is regular window
|
||||
if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_groupRules & Desktop::View::GROUP_SET_ALWAYS)) {
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args);
|
||||
g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args);
|
||||
PWINDOW->warpCursor();
|
||||
} else
|
||||
moveWindowOutOfGroup(PWINDOW, args);
|
||||
} else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) { // no target window
|
||||
moveWindowOutOfGroup(PWINDOW, args);
|
||||
} else if (!PWINDOWINDIR && !ISWINDOWGROUP) { // no target in dir and not in group
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args);
|
||||
g_layoutManager->moveInDirection(PWINDOW->layoutTarget(), args);
|
||||
PWINDOW->warpCursor();
|
||||
}
|
||||
|
||||
|
|
@ -3054,13 +2878,13 @@ SDispatchResult CKeybindManager::setIgnoreGroupLock(std::string args) {
|
|||
|
||||
SDispatchResult CKeybindManager::denyWindowFromGroup(std::string args) {
|
||||
const auto PWINDOW = Desktop::focusState()->window();
|
||||
if (!PWINDOW || (PWINDOW && PWINDOW->m_groupData.pNextWindow.lock()))
|
||||
if (!PWINDOW || (PWINDOW && PWINDOW->m_group))
|
||||
return {};
|
||||
|
||||
if (args == "toggle")
|
||||
PWINDOW->m_groupData.deny = !PWINDOW->m_groupData.deny;
|
||||
PWINDOW->m_group->setDenied(!PWINDOW->m_group->denied());
|
||||
else
|
||||
PWINDOW->m_groupData.deny = args == "on";
|
||||
PWINDOW->m_group->setDenied(args == "on");
|
||||
|
||||
PWINDOW->updateDecorationValues();
|
||||
|
||||
|
|
@ -3090,16 +2914,15 @@ SDispatchResult CKeybindManager::moveGroupWindow(std::string args) {
|
|||
if (!PLASTWINDOW)
|
||||
return {.success = false, .error = "No window found"};
|
||||
|
||||
if (!PLASTWINDOW->m_groupData.pNextWindow.lock())
|
||||
if (!PLASTWINDOW->m_group)
|
||||
return {.success = false, .error = "Window not in a group"};
|
||||
|
||||
if ((!BACK && PLASTWINDOW->m_groupData.pNextWindow->m_groupData.head) || (BACK && PLASTWINDOW->m_groupData.head)) {
|
||||
std::swap(PLASTWINDOW->m_groupData.head, PLASTWINDOW->m_groupData.pNextWindow->m_groupData.head);
|
||||
std::swap(PLASTWINDOW->m_groupData.locked, PLASTWINDOW->m_groupData.pNextWindow->m_groupData.locked);
|
||||
} else
|
||||
PLASTWINDOW->switchWithWindowInGroup(BACK ? PLASTWINDOW->getGroupPrevious() : PLASTWINDOW->m_groupData.pNextWindow.lock());
|
||||
const auto GROUP = PLASTWINDOW->m_group;
|
||||
|
||||
PLASTWINDOW->updateWindowDecos();
|
||||
if (BACK)
|
||||
GROUP->swapWithLast();
|
||||
else
|
||||
GROUP->swapWithNext();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
@ -3298,16 +3121,18 @@ SDispatchResult CKeybindManager::setProp(std::string args) {
|
|||
|
||||
if (PWINDOW->m_ruleApplicator->noFocus().valueOrDefault() != noFocus) {
|
||||
// FIXME: what the fuck is going on here? -vax
|
||||
Desktop::focusState()->rawWindowFocus(nullptr);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW);
|
||||
Desktop::focusState()->rawWindowFocus(nullptr, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(PWINDOW, Desktop::FOCUS_REASON_KEYBIND);
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_KEYBIND);
|
||||
}
|
||||
|
||||
if (PROP == "no_vrr")
|
||||
g_pConfigManager->ensureVRR(PWINDOW->m_monitor.lock());
|
||||
|
||||
for (auto const& m : g_pCompositor->m_monitors)
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(m->m_id);
|
||||
for (auto const& m : g_pCompositor->m_monitors) {
|
||||
if (m->m_activeWorkspace)
|
||||
m->m_activeWorkspace->m_space->recalculate();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
#include "LayoutManager.hpp"
|
||||
|
||||
CLayoutManager::CLayoutManager() {
|
||||
m_layouts.emplace_back(std::make_pair<>("dwindle", &m_dwindleLayout));
|
||||
m_layouts.emplace_back(std::make_pair<>("master", &m_masterLayout));
|
||||
}
|
||||
|
||||
IHyprLayout* CLayoutManager::getCurrentLayout() {
|
||||
return m_layouts[m_currentLayoutID].second;
|
||||
}
|
||||
|
||||
void CLayoutManager::switchToLayout(std::string layout) {
|
||||
for (size_t i = 0; i < m_layouts.size(); ++i) {
|
||||
if (m_layouts[i].first == layout) {
|
||||
if (i == sc<size_t>(m_currentLayoutID))
|
||||
return;
|
||||
|
||||
getCurrentLayout()->onDisable();
|
||||
m_currentLayoutID = i;
|
||||
getCurrentLayout()->onEnable();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log::logger->log(Log::ERR, "Unknown layout!");
|
||||
}
|
||||
|
||||
bool CLayoutManager::addLayout(const std::string& name, IHyprLayout* layout) {
|
||||
if (std::ranges::find_if(m_layouts, [&](const auto& other) { return other.first == name || other.second == layout; }) != m_layouts.end())
|
||||
return false;
|
||||
|
||||
m_layouts.emplace_back(std::make_pair<>(name, layout));
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Added new layout {} at {:x}", name, rc<uintptr_t>(layout));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLayoutManager::removeLayout(IHyprLayout* layout) {
|
||||
const auto IT = std::ranges::find_if(m_layouts, [&](const auto& other) { return other.second == layout; });
|
||||
|
||||
if (IT == m_layouts.end() || IT->first == "dwindle" || IT->first == "master")
|
||||
return false;
|
||||
|
||||
if (m_currentLayoutID == IT - m_layouts.begin())
|
||||
switchToLayout("dwindle");
|
||||
|
||||
Log::logger->log(Log::DEBUG, "Removed a layout {} at {:x}", IT->first, rc<uintptr_t>(layout));
|
||||
|
||||
std::erase(m_layouts, *IT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> CLayoutManager::getAllLayoutNames() {
|
||||
std::vector<std::string> results(m_layouts.size());
|
||||
for (size_t i = 0; i < m_layouts.size(); ++i)
|
||||
results[i] = m_layouts[i].first;
|
||||
return results;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../layout/DwindleLayout.hpp"
|
||||
#include "../layout/MasterLayout.hpp"
|
||||
|
||||
class CLayoutManager {
|
||||
public:
|
||||
CLayoutManager();
|
||||
|
||||
IHyprLayout* getCurrentLayout();
|
||||
|
||||
void switchToLayout(std::string);
|
||||
|
||||
bool addLayout(const std::string& name, IHyprLayout* layout);
|
||||
bool removeLayout(IHyprLayout* layout);
|
||||
std::vector<std::string> getAllLayoutNames();
|
||||
|
||||
private:
|
||||
enum eHyprLayouts : uint8_t {
|
||||
LAYOUT_DWINDLE = 0,
|
||||
LAYOUT_MASTER
|
||||
};
|
||||
|
||||
int m_currentLayoutID = LAYOUT_DWINDLE;
|
||||
|
||||
CHyprDwindleLayout m_dwindleLayout;
|
||||
CHyprMasterLayout m_masterLayout;
|
||||
std::vector<std::pair<std::string, IHyprLayout*>> m_layouts;
|
||||
};
|
||||
|
||||
inline UP<CLayoutManager> g_pLayoutManager;
|
||||
|
|
@ -668,7 +668,7 @@ void CSeatManager::setGrab(SP<CSeatGrab> grab) {
|
|||
|
||||
// If this was a popup grab, focus its parent window to maintain context
|
||||
if (validMapped(parentWindow)) {
|
||||
Desktop::focusState()->rawWindowFocus(parentWindow);
|
||||
Desktop::focusState()->rawWindowFocus(parentWindow, Desktop::FOCUS_REASON_FFM);
|
||||
Log::logger->log(Log::DEBUG, "[seatmgr] Refocused popup parent window {} (follow_mouse={})", parentWindow->m_title, *PFOLLOWMOUSE);
|
||||
} else
|
||||
g_pInputManager->refocusLastWindow(PMONITOR);
|
||||
|
|
@ -702,7 +702,7 @@ void CSeatManager::setGrab(SP<CSeatGrab> grab) {
|
|||
auto candidate = Desktop::focusState()->window();
|
||||
|
||||
if (candidate)
|
||||
Desktop::focusState()->rawWindowFocus(candidate);
|
||||
Desktop::focusState()->rawWindowFocus(candidate, Desktop::FOCUS_REASON_FFM);
|
||||
}
|
||||
|
||||
if (oldGrab->m_onEnd)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "../../desktop/view/LayerSurface.hpp"
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
#include "../../desktop/view/Group.hpp"
|
||||
#include "../../desktop/Workspace.hpp"
|
||||
|
||||
#include "../../config/ConfigManager.hpp"
|
||||
|
|
@ -469,7 +470,7 @@ void CDesktopAnimationManager::setFullscreenFadeAnimation(PHLWORKSPACE ws, eAnim
|
|||
*w->m_alpha = 1.F;
|
||||
else if (!w->isFullscreen()) {
|
||||
const bool CREATED_OVER_FS = w->m_createdOverFullscreen;
|
||||
const bool IS_IN_GROUP_OF_FS = FSWINDOW && FSWINDOW->hasInGroup(w);
|
||||
const bool IS_IN_GROUP_OF_FS = FSWINDOW && FSWINDOW->m_group && FSWINDOW->m_group->has(w);
|
||||
*w->m_alpha = !CREATED_OVER_FS && !IS_IN_GROUP_OF_FS ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,13 @@
|
|||
#include "../../render/Renderer.hpp"
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../managers/EventManager.hpp"
|
||||
#include "../../managers/LayoutManager.hpp"
|
||||
#include "../../managers/permissions/DynamicPermissionManager.hpp"
|
||||
|
||||
#include "../../helpers/time/Time.hpp"
|
||||
#include "../../helpers/MiscFunctions.hpp"
|
||||
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
|
||||
#include "trackpad/TrackpadGestures.hpp"
|
||||
#include "../cursor/CursorShapeOverrideController.hpp"
|
||||
|
||||
|
|
@ -230,6 +231,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
Vector2D surfacePos = Vector2D(-1337, -1337);
|
||||
PHLWINDOW pFoundWindow;
|
||||
PHLLS pFoundLayerSurface;
|
||||
const auto FOCUS_REASON = refocus ? Desktop::FOCUS_REASON_CLICK : Desktop::FOCUS_REASON_FFM;
|
||||
|
||||
EMIT_HOOK_EVENT_CANCELLABLE("mouseMove", MOUSECOORDSFLOORED);
|
||||
|
||||
|
|
@ -365,7 +367,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
}
|
||||
}
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal());
|
||||
g_layoutManager->moveMouse(getMouseCoordsInternal());
|
||||
|
||||
// forced above all
|
||||
if (!g_pInputManager->m_exclusiveLSes.empty()) {
|
||||
|
|
@ -522,7 +524,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
g_pSeatManager->setPointerFocus(nullptr, {});
|
||||
|
||||
if (refocus || !Desktop::focusState()->window()) // if we are forcing a refocus, and we don't find a surface, clear the kb focus too!
|
||||
Desktop::focusState()->rawWindowFocus(nullptr);
|
||||
Desktop::focusState()->rawWindowFocus(nullptr, FOCUS_REASON);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -550,7 +552,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
m_foundSurfaceToFocus = foundSurface;
|
||||
}
|
||||
|
||||
if (m_currentlyDraggedWindow.lock() && pFoundWindow != m_currentlyDraggedWindow) {
|
||||
if (g_layoutManager->dragController()->target() && pFoundWindow != g_layoutManager->dragController()->target()) {
|
||||
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
|
||||
return;
|
||||
}
|
||||
|
|
@ -582,7 +584,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
((pFoundWindow->m_isFloating && *PFLOATBEHAVIOR == 2) || (Desktop::focusState()->window()->m_isFloating != pFoundWindow->m_isFloating && *PFLOATBEHAVIOR != 0))) {
|
||||
// enter if change floating style
|
||||
if (FOLLOWMOUSE != 3 && allowKeyboardRefocus)
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, foundSurface);
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface);
|
||||
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
|
||||
} else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3)
|
||||
g_pSeatManager->setPointerFocus(foundSurface, surfaceLocal);
|
||||
|
|
@ -610,7 +612,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
const bool hasNoFollowMouse = pFoundWindow && pFoundWindow->m_ruleApplicator->noFollowMouse().valueOrDefault();
|
||||
|
||||
if (refocus || !hasNoFollowMouse)
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, foundSurface);
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface);
|
||||
}
|
||||
} else
|
||||
Desktop::focusState()->rawSurfaceFocus(foundSurface, pFoundWindow);
|
||||
|
|
@ -619,7 +621,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus, bool mouse, st
|
|||
}
|
||||
|
||||
if (g_pSeatManager->m_state.keyboardFocus == nullptr)
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, foundSurface);
|
||||
Desktop::focusState()->rawWindowFocus(pFoundWindow, FOCUS_REASON, foundSurface);
|
||||
|
||||
m_lastFocusOnLS = false;
|
||||
} else {
|
||||
|
|
@ -1629,13 +1631,13 @@ bool CInputManager::refocusLastWindow(PHLMONITOR pMonitor) {
|
|||
if (!foundSurface && Desktop::focusState()->window() && Desktop::focusState()->window()->m_workspace && Desktop::focusState()->window()->m_workspace->isVisibleNotCovered()) {
|
||||
// then the last focused window if we're on the same workspace as it
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_FFM);
|
||||
} else {
|
||||
// otherwise fall back to a normal refocus.
|
||||
|
||||
if (foundSurface && !foundSurface->m_hlSurface->keyboardFocusable()) {
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW);
|
||||
Desktop::focusState()->fullWindowFocus(PLASTWINDOW, Desktop::FOCUS_REASON_FFM);
|
||||
}
|
||||
|
||||
refocus();
|
||||
|
|
@ -1951,7 +1953,7 @@ void CInputManager::setCursorIconOnBorder(PHLWINDOW w) {
|
|||
|
||||
if (w->hasPopupAt(mouseCoords))
|
||||
direction = BORDERICON_NONE;
|
||||
else if (!boxFullGrabInput.containsPoint(mouseCoords) || (!m_currentlyHeldButtons.empty() && m_currentlyDraggedWindow.expired()))
|
||||
else if (!boxFullGrabInput.containsPoint(mouseCoords) || (!m_currentlyHeldButtons.empty() && !g_layoutManager->dragController()->target()))
|
||||
direction = BORDERICON_NONE;
|
||||
else {
|
||||
|
||||
|
|
|
|||
|
|
@ -154,12 +154,6 @@ class CInputManager {
|
|||
|
||||
STouchData m_touchData;
|
||||
|
||||
// for dragging floating windows
|
||||
PHLWINDOWREF m_currentlyDraggedWindow;
|
||||
eMouseBindMode m_dragMode = MBIND_INVALID;
|
||||
bool m_wasDraggingWindow = false;
|
||||
bool m_dragThresholdReached = false;
|
||||
|
||||
// for refocus to be forced
|
||||
PHLWINDOWREF m_forcedFocus;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
#include "CloseGesture.hpp"
|
||||
|
||||
#include "../../../../Compositor.hpp"
|
||||
#include "../../../../managers/LayoutManager.hpp"
|
||||
#include "../../../../managers/animation/DesktopAnimationManager.hpp"
|
||||
#include "../../../../render/Renderer.hpp"
|
||||
#include "../../../../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../../../../managers/eventLoop/EventLoopTimer.hpp"
|
||||
#include "../../../../config/ConfigValue.hpp"
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../layout/target/Target.hpp"
|
||||
|
||||
constexpr const float MAX_DISTANCE = 200.F;
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ void CCloseTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e)
|
|||
if (!window->m_isMapped)
|
||||
return;
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(window.lock());
|
||||
window->layoutTarget()->recalc();
|
||||
window->updateDecorationValues();
|
||||
window->sendWindowSize(true);
|
||||
*window->m_alpha = 1.F;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#include "FloatGesture.hpp"
|
||||
|
||||
#include "../../../../managers/LayoutManager.hpp"
|
||||
#include "../../../../render/Renderer.hpp"
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../desktop/view/Window.hpp"
|
||||
#include "../../../../layout/LayoutManager.hpp"
|
||||
#include "../../../../layout/target/WindowTarget.hpp"
|
||||
|
||||
constexpr const float MAX_DISTANCE = 250.F;
|
||||
|
||||
|
|
@ -40,8 +41,7 @@ void CFloatTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin&
|
|||
return;
|
||||
}
|
||||
|
||||
m_window->m_isFloating = !m_window->m_isFloating;
|
||||
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(m_window.lock());
|
||||
g_layoutManager->changeFloatingMode(m_window->layoutTarget());
|
||||
|
||||
m_posFrom = m_window->m_realPosition->begun();
|
||||
m_sizeFrom = m_window->m_realSize->begun();
|
||||
|
|
@ -79,8 +79,7 @@ void CFloatTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e)
|
|||
if (COMPLETION < 0.2F) {
|
||||
// revert the animation
|
||||
g_pHyprRenderer->damageWindow(m_window.lock());
|
||||
m_window->m_isFloating = !m_window->m_isFloating;
|
||||
g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(m_window.lock());
|
||||
g_layoutManager->changeFloatingMode(m_window->layoutTarget());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../desktop/view/Window.hpp"
|
||||
#include "../../../../managers/LayoutManager.hpp"
|
||||
#include "../../../../render/Renderer.hpp"
|
||||
#include "../../../../layout/LayoutManager.hpp"
|
||||
|
||||
void CMoveTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
|
||||
ITrackpadGesture::begin(e);
|
||||
|
|
@ -19,7 +19,7 @@ void CMoveTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpdate
|
|||
const auto DELTA = e.swipe ? e.swipe->delta : e.pinch->delta;
|
||||
|
||||
if (m_window->m_isFloating) {
|
||||
g_pLayoutManager->getCurrentLayout()->moveActiveWindow(DELTA, m_window.lock());
|
||||
g_layoutManager->moveTarget(DELTA, m_window->layoutTarget());
|
||||
m_window->m_realSize->warp();
|
||||
m_window->m_realPosition->warp();
|
||||
return;
|
||||
|
|
@ -52,10 +52,10 @@ void CMoveTrackpadGesture::end(const ITrackpadGesture::STrackpadGestureEnd& e) {
|
|||
|
||||
if (std::abs(m_lastDelta.x) > std::abs(m_lastDelta.y)) {
|
||||
// horizontal
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(m_window.lock(), m_lastDelta.x > 0 ? "r" : "l");
|
||||
g_layoutManager->moveInDirection(m_window->layoutTarget(), m_lastDelta.x > 0 ? "r" : "l");
|
||||
} else {
|
||||
// vertical
|
||||
g_pLayoutManager->getCurrentLayout()->moveWindowTo(m_window.lock(), m_lastDelta.y > 0 ? "b" : "t");
|
||||
g_layoutManager->moveInDirection(m_window->layoutTarget(), m_lastDelta.y > 0 ? "b" : "t");
|
||||
}
|
||||
|
||||
const auto GOAL = m_window->m_realPosition->goal();
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include "../../../../desktop/state/FocusState.hpp"
|
||||
#include "../../../../desktop/view/Window.hpp"
|
||||
#include "../../../../managers/LayoutManager.hpp"
|
||||
#include "../../../../render/Renderer.hpp"
|
||||
#include "../../../../layout/LayoutManager.hpp"
|
||||
|
||||
void CResizeTrackpadGesture::begin(const ITrackpadGesture::STrackpadGestureBegin& e) {
|
||||
ITrackpadGesture::begin(e);
|
||||
|
|
@ -17,8 +17,8 @@ void CResizeTrackpadGesture::update(const ITrackpadGesture::STrackpadGestureUpda
|
|||
|
||||
g_pHyprRenderer->damageWindow(m_window.lock());
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->resizeActiveWindow((e.swipe ? e.swipe->delta : e.pinch->delta),
|
||||
cornerFromBox(m_window->getWindowMainSurfaceBox(), g_pInputManager->getMouseCoordsInternal()), m_window.lock());
|
||||
g_layoutManager->resizeTarget((e.swipe ? e.swipe->delta : e.pinch->delta), m_window->layoutTarget(),
|
||||
Layout::cornerFromBox(m_window->getWindowMainSurfaceBox(), g_pInputManager->getMouseCoordsInternal()));
|
||||
m_window->m_realSize->warp();
|
||||
m_window->m_realPosition->warp();
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
#include "../debug/HyprCtl.hpp"
|
||||
#include "../plugins/PluginSystem.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../debug/HyprNotificationOverlay.hpp"
|
||||
#include "../layout/target/Target.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
#include <dlfcn.h>
|
||||
#include <filesystem>
|
||||
|
||||
|
|
@ -62,25 +63,44 @@ APICALL std::string HyprlandAPI::invokeHyprctlCommand(const std::string& call, c
|
|||
}
|
||||
|
||||
APICALL bool HyprlandAPI::addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout) {
|
||||
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||
|
||||
if (!PLUGIN)
|
||||
return false;
|
||||
|
||||
PLUGIN->m_registeredLayouts.push_back(layout);
|
||||
|
||||
return g_pLayoutManager->addLayout(name, layout);
|
||||
return false;
|
||||
}
|
||||
|
||||
APICALL bool HyprlandAPI::removeLayout(HANDLE handle, IHyprLayout* layout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
APICALL bool HyprlandAPI::addTiledAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function<UP<Layout::ITiledAlgorithm>()>&& factory) {
|
||||
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||
|
||||
if (!PLUGIN)
|
||||
return false;
|
||||
|
||||
std::erase(PLUGIN->m_registeredLayouts, layout);
|
||||
PLUGIN->m_registeredAlgos.emplace_back(name);
|
||||
|
||||
return g_pLayoutManager->removeLayout(layout);
|
||||
return Layout::Supplementary::algoMatcher()->registerTiledAlgo(name, typeInfo, std::move(factory));
|
||||
}
|
||||
|
||||
APICALL bool HyprlandAPI::addFloatingAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function<UP<Layout::IFloatingAlgorithm>()>&& factory) {
|
||||
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||
|
||||
if (!PLUGIN)
|
||||
return false;
|
||||
|
||||
PLUGIN->m_registeredAlgos.emplace_back(name);
|
||||
|
||||
return Layout::Supplementary::algoMatcher()->registerFloatingAlgo(name, typeInfo, std::move(factory));
|
||||
}
|
||||
|
||||
APICALL bool HyprlandAPI::removeAlgo(HANDLE handle, const std::string& name) {
|
||||
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
|
||||
|
||||
if (!PLUGIN)
|
||||
return false;
|
||||
|
||||
std::erase(PLUGIN->m_registeredAlgos, name);
|
||||
|
||||
return Layout::Supplementary::algoMatcher()->unregisterAlgo(name);
|
||||
}
|
||||
|
||||
APICALL bool HyprlandAPI::reloadConfig() {
|
||||
|
|
@ -130,7 +150,7 @@ APICALL bool HyprlandAPI::addWindowDecoration(HANDLE handle, PHLWINDOW pWindow,
|
|||
|
||||
pWindow->addWindowDeco(std::move(pDecoration));
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
|
||||
pWindow->layoutTarget()->recalc();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,11 @@ class IHyprLayout;
|
|||
class IHyprWindowDecoration;
|
||||
struct SConfigValue;
|
||||
|
||||
namespace Layout {
|
||||
class ITiledAlgorithm;
|
||||
class IFloatingAlgorithm;
|
||||
};
|
||||
|
||||
/*
|
||||
These methods are for the plugin to implement
|
||||
Methods marked with REQUIRED are required.
|
||||
|
|
@ -172,15 +177,26 @@ namespace HyprlandAPI {
|
|||
Adds a layout to Hyprland.
|
||||
|
||||
returns: true on success. False otherwise.
|
||||
|
||||
deprecated: addTiledAlgo, addFloatingAlgo
|
||||
*/
|
||||
APICALL bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout);
|
||||
APICALL [[deprecated]] bool addLayout(HANDLE handle, const std::string& name, IHyprLayout* layout);
|
||||
|
||||
/*
|
||||
Removes an added layout from Hyprland.
|
||||
|
||||
returns: true on success. False otherwise.
|
||||
|
||||
deprecated: V2 removeAlgo
|
||||
*/
|
||||
APICALL bool removeLayout(HANDLE handle, IHyprLayout* layout);
|
||||
APICALL [[deprecated]] bool removeLayout(HANDLE handle, IHyprLayout* layout);
|
||||
|
||||
/*
|
||||
Algorithm fns. Used for registering and removing. Return success.
|
||||
*/
|
||||
APICALL bool addTiledAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function<UP<Layout::ITiledAlgorithm>()>&& factory);
|
||||
APICALL bool addFloatingAlgo(HANDLE handle, const std::string& name, const std::type_info* typeInfo, std::function<UP<Layout::IFloatingAlgorithm>()>&& factory);
|
||||
APICALL bool removeAlgo(HANDLE handle, const std::string& name);
|
||||
|
||||
/*
|
||||
Queues a config reload. Does not take effect immediately.
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
#include <ranges>
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../debug/HyprCtl.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/eventLoop/EventLoopManager.hpp"
|
||||
#include "../managers/permissions/DynamicPermissionManager.hpp"
|
||||
#include "../debug/HyprNotificationOverlay.hpp"
|
||||
#include "../layout/supplementary/WorkspaceAlgoMatcher.hpp"
|
||||
#include "../i18n/Engine.hpp"
|
||||
|
||||
CPluginSystem::CPluginSystem() {
|
||||
|
|
@ -156,9 +156,9 @@ void CPluginSystem::unloadPlugin(const CPlugin* plugin, bool eject) {
|
|||
g_pHookSystem->unhook(SHP);
|
||||
}
|
||||
|
||||
const auto ls = plugin->m_registeredLayouts;
|
||||
for (auto const& l : ls)
|
||||
g_pLayoutManager->removeLayout(l);
|
||||
for (const auto& l : plugin->m_registeredAlgos) {
|
||||
Layout::Supplementary::algoMatcher()->unregisterAlgo(l);
|
||||
}
|
||||
|
||||
g_pFunctionHookSystem->removeAllHooksFrom(plugin->m_handle);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ class CPlugin {
|
|||
|
||||
HANDLE m_handle = nullptr;
|
||||
|
||||
std::vector<IHyprLayout*> m_registeredLayouts;
|
||||
std::vector<IHyprWindowDecoration*> m_registeredDecorations;
|
||||
std::vector<std::pair<std::string, WP<HOOK_CALLBACK_FN>>> m_registeredCallbacks;
|
||||
std::vector<std::string> m_registeredDispatchers;
|
||||
std::vector<WP<SHyprCtlCommand>> m_registeredHyprctlCommands;
|
||||
std::vector<std::string> m_registeredAlgos;
|
||||
};
|
||||
|
||||
class CPluginSystem {
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ CForeignToplevelWlrProtocol::CForeignToplevelWlrProtocol(const wl_interface* ifa
|
|||
});
|
||||
|
||||
static auto P3 = g_pHookSystem->hookDynamic("activeWindow", [this](void* self, SCallbackInfo& info, std::any data) {
|
||||
const auto PWINDOW = std::any_cast<PHLWINDOW>(data);
|
||||
const auto PWINDOW = std::any_cast<Desktop::View::SWindowActiveEvent>(data).window;
|
||||
|
||||
if (PWINDOW && !windowValidForForeign(PWINDOW))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#include "../managers/input/InputManager.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/animation/AnimationManager.hpp"
|
||||
#include "../managers/LayoutManager.hpp"
|
||||
#include "../desktop/view/Window.hpp"
|
||||
#include "../desktop/view/LayerSurface.hpp"
|
||||
#include "../desktop/view/GlobalViewMethods.hpp"
|
||||
|
|
@ -28,6 +27,8 @@
|
|||
#include "../hyprerror/HyprError.hpp"
|
||||
#include "../debug/HyprDebugOverlay.hpp"
|
||||
#include "../debug/HyprNotificationOverlay.hpp"
|
||||
#include "../layout/LayoutManager.hpp"
|
||||
#include "../layout/space/Space.hpp"
|
||||
#include "../i18n/Engine.hpp"
|
||||
#include "helpers/CursorShapes.hpp"
|
||||
#include "helpers/Monitor.hpp"
|
||||
|
|
@ -1305,7 +1306,7 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
|
|||
|
||||
if (pMonitor->m_scheduledRecalc) {
|
||||
pMonitor->m_scheduledRecalc = false;
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitor->m_id);
|
||||
pMonitor->m_activeWorkspace->m_space->recalculate();
|
||||
}
|
||||
|
||||
if (!pMonitor->m_output->needsFrame && pMonitor->m_forceFullFrames == 0)
|
||||
|
|
@ -1906,7 +1907,7 @@ void CHyprRenderer::arrangeLayersForMonitor(const MONITORID& monitor) {
|
|||
// damage the monitor if can
|
||||
damageMonitor(PMONITOR);
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitor);
|
||||
g_layoutManager->invalidateMonitorGeometries(PMONITOR);
|
||||
}
|
||||
|
||||
void CHyprRenderer::damageSurface(SP<CWLSurfaceResource> pSurface, double x, double y, double scale) {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,15 @@
|
|||
#include "../../Compositor.hpp"
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
#include "../../desktop/state/FocusState.hpp"
|
||||
#include "managers/LayoutManager.hpp"
|
||||
#include "../../desktop/view/Group.hpp"
|
||||
#include <ranges>
|
||||
#include <pango/pangocairo.h>
|
||||
#include "../pass/TexPassElement.hpp"
|
||||
#include "../pass/RectPassElement.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
#include "../../layout/supplementary/DragController.hpp"
|
||||
|
||||
// shared things to conserve VRAM
|
||||
static SP<CTexture> m_tGradientActive = makeShared<CTexture>();
|
||||
|
|
@ -65,19 +67,14 @@ eDecorationType CHyprGroupBarDecoration::getDecorationType() {
|
|||
//
|
||||
|
||||
void CHyprGroupBarDecoration::updateWindow(PHLWINDOW pWindow) {
|
||||
if (m_window->m_groupData.pNextWindow.expired()) {
|
||||
if (!m_window->m_group) {
|
||||
m_window->removeWindowDeco(this);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dwGroupMembers.clear();
|
||||
PHLWINDOW head = pWindow->getGroupHead();
|
||||
m_dwGroupMembers.emplace_back(head);
|
||||
|
||||
PHLWINDOW curr = head->m_groupData.pNextWindow.lock();
|
||||
while (curr != head) {
|
||||
m_dwGroupMembers.emplace_back(curr);
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
for (const auto& w : m_window->m_group->windows()) {
|
||||
m_dwGroupMembers.emplace_back(w);
|
||||
}
|
||||
|
||||
damageEntire();
|
||||
|
|
@ -158,7 +155,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
|
|||
|
||||
rect.scale(pMonitor->m_scale).round();
|
||||
|
||||
const bool GROUPLOCKED = m_window->getGroupHead()->m_groupData.locked || g_pKeybindManager->m_groupsLocked;
|
||||
const bool GROUPLOCKED = m_window->m_group->locked() || g_pKeybindManager->m_groupsLocked;
|
||||
const auto* const PCOLACTIVE = GROUPLOCKED ? GROUPCOLACTIVELOCKED : GROUPCOLACTIVE;
|
||||
const auto* const PCOLINACTIVE = GROUPLOCKED ? GROUPCOLINACTIVELOCKED : GROUPCOLINACTIVE;
|
||||
|
||||
|
|
@ -204,6 +201,7 @@ void CHyprGroupBarDecoration::draw(PHLMONITOR pMonitor, float const& a) {
|
|||
data.tex = GRADIENTTEX;
|
||||
data.blur = blur;
|
||||
data.box = rect;
|
||||
data.a = a;
|
||||
if (*PGRADIENTROUNDING) {
|
||||
data.round = *PGRADIENTROUNDING;
|
||||
data.roundingPower = *PGRADIENTROUNDINGPOWER;
|
||||
|
|
@ -391,7 +389,7 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
|
|||
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
|
||||
static auto POUTERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_out");
|
||||
static auto PINNERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_in");
|
||||
if (m_window.lock() == m_window->m_groupData.pNextWindow.lock())
|
||||
if (m_window->m_group->size() == 1)
|
||||
return false;
|
||||
|
||||
const float BARRELATIVEX = pos.x - assignedBoxGlobal().x;
|
||||
|
|
@ -404,95 +402,33 @@ bool CHyprGroupBarDecoration::onBeginWindowDragOnDeco(const Vector2D& pos) {
|
|||
if (*PSTACKED && (BARRELATIVEY - (m_barHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP))
|
||||
return false;
|
||||
|
||||
PHLWINDOW pWindow = m_window->getGroupWindowByIndex(WINDOWINDEX);
|
||||
PHLWINDOW pWindow = m_window->m_group->fromIndex(WINDOWINDEX);
|
||||
|
||||
// hack
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow);
|
||||
if (!pWindow->m_isFloating) {
|
||||
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_groupsLocked;
|
||||
g_pKeybindManager->m_groupsLocked = true;
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreated(pWindow);
|
||||
g_pKeybindManager->m_groupsLocked = GROUPSLOCKEDPREV;
|
||||
}
|
||||
const auto& GROUP = m_window->m_group;
|
||||
|
||||
g_pInputManager->m_currentlyDraggedWindow = pWindow;
|
||||
// remove the window from the group
|
||||
GROUP->remove(pWindow);
|
||||
|
||||
// start a move drag on it
|
||||
g_layoutManager->dragController()->dragBegin(pWindow->layoutTarget(), MBIND_MOVE);
|
||||
|
||||
if (!g_pCompositor->isWindowActive(pWindow))
|
||||
Desktop::focusState()->rawWindowFocus(pWindow);
|
||||
Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHyprGroupBarDecoration::onEndWindowDragOnDeco(const Vector2D& pos, PHLWINDOW pDraggedWindow) {
|
||||
static auto PSTACKED = CConfigValue<Hyprlang::INT>("group:groupbar:stacked");
|
||||
static auto PDRAGINTOGROUP = CConfigValue<Hyprlang::INT>("group:drag_into_group");
|
||||
static auto PMERGEFLOATEDINTOTILEDONGROUPBAR = CConfigValue<Hyprlang::INT>("group:merge_floated_into_tiled_on_groupbar");
|
||||
static auto PMERGEGROUPSONGROUPBAR = CConfigValue<Hyprlang::INT>("group:merge_groups_on_groupbar");
|
||||
static auto POUTERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_out");
|
||||
static auto PINNERGAP = CConfigValue<Hyprlang::INT>("group:groupbar:gaps_in");
|
||||
const bool FLOATEDINTOTILED = !m_window->m_isFloating && !pDraggedWindow->m_draggingTiled;
|
||||
const bool FLOATEDINTOTILED = !m_window->m_isFloating && !g_layoutManager->dragController()->draggingTiled();
|
||||
|
||||
g_pInputManager->m_wasDraggingWindow = false;
|
||||
|
||||
if (!pDraggedWindow->canBeGroupedInto(m_window.lock()) || (*PDRAGINTOGROUP != 1 && *PDRAGINTOGROUP != 2) || (FLOATEDINTOTILED && !*PMERGEFLOATEDINTOTILEDONGROUPBAR) ||
|
||||
(!*PMERGEGROUPSONGROUPBAR && pDraggedWindow->m_groupData.pNextWindow.lock() && m_window->m_groupData.pNextWindow.lock())) {
|
||||
g_pInputManager->m_wasDraggingWindow = true;
|
||||
if (!pDraggedWindow->canBeGroupedInto(m_window->m_group) || (*PDRAGINTOGROUP != 1 && *PDRAGINTOGROUP != 2) || (FLOATEDINTOTILED && !*PMERGEFLOATEDINTOTILEDONGROUPBAR) ||
|
||||
(!*PMERGEGROUPSONGROUPBAR && pDraggedWindow->m_group))
|
||||
return false;
|
||||
}
|
||||
|
||||
const float BARRELATIVE = *PSTACKED ? pos.y - assignedBoxGlobal().y - (m_barHeight + *POUTERGAP) / 2 : pos.x - assignedBoxGlobal().x - m_barWidth / 2;
|
||||
const float BARSIZE = *PSTACKED ? m_barHeight + *POUTERGAP : m_barWidth + *PINNERGAP;
|
||||
const int WINDOWINDEX = BARRELATIVE < 0 ? -1 : BARRELATIVE / BARSIZE;
|
||||
|
||||
PHLWINDOW pWindowInsertAfter = m_window->getGroupWindowByIndex(WINDOWINDEX);
|
||||
PHLWINDOW pWindowInsertEnd = pWindowInsertAfter->m_groupData.pNextWindow.lock();
|
||||
PHLWINDOW pDraggedHead = pDraggedWindow->m_groupData.pNextWindow.lock() ? pDraggedWindow->getGroupHead() : pDraggedWindow;
|
||||
|
||||
if (!pDraggedWindow->m_groupData.pNextWindow.expired()) {
|
||||
|
||||
// stores group data
|
||||
std::vector<PHLWINDOW> members;
|
||||
PHLWINDOW curr = pDraggedHead;
|
||||
const bool WASLOCKED = pDraggedHead->m_groupData.locked;
|
||||
do {
|
||||
members.push_back(curr);
|
||||
curr = curr->m_groupData.pNextWindow.lock();
|
||||
} while (curr != members[0]);
|
||||
|
||||
// removes all windows
|
||||
for (const PHLWINDOW& w : members) {
|
||||
w->m_groupData.pNextWindow.reset();
|
||||
w->m_groupData.head = false;
|
||||
w->m_groupData.locked = false;
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(w);
|
||||
}
|
||||
|
||||
// restores the group
|
||||
for (auto it = members.begin(); it != members.end(); ++it) {
|
||||
(*it)->m_isFloating = pWindowInsertAfter->m_isFloating; // match the floating state of group members
|
||||
*(*it)->m_realSize = pWindowInsertAfter->m_realSize->goal(); // match the size of group members
|
||||
*(*it)->m_realPosition = pWindowInsertAfter->m_realPosition->goal(); // match the position of group members
|
||||
if (std::next(it) != members.end())
|
||||
(*it)->m_groupData.pNextWindow = *std::next(it);
|
||||
else
|
||||
(*it)->m_groupData.pNextWindow = members[0];
|
||||
}
|
||||
members[0]->m_groupData.head = true;
|
||||
members[0]->m_groupData.locked = WASLOCKED;
|
||||
} else
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pDraggedWindow);
|
||||
|
||||
pDraggedWindow->m_isFloating = pWindowInsertAfter->m_isFloating; // match the floating state of the window
|
||||
|
||||
pWindowInsertAfter->insertWindowToGroup(pDraggedWindow);
|
||||
|
||||
if (WINDOWINDEX == -1)
|
||||
std::swap(pDraggedHead->m_groupData.head, pWindowInsertEnd->m_groupData.head);
|
||||
|
||||
m_window->setGroupCurrent(pDraggedWindow);
|
||||
pDraggedWindow->applyGroupRules();
|
||||
pDraggedWindow->updateWindowDecos();
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pDraggedWindow);
|
||||
m_window->m_group->add(pDraggedWindow);
|
||||
|
||||
if (!pDraggedWindow->getDecorationByType(DECORATION_GROUPBAR))
|
||||
pDraggedWindow->addWindowDeco(makeUnique<CHyprGroupBarDecoration>(pDraggedWindow));
|
||||
|
|
@ -519,7 +455,7 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo
|
|||
if (e.state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
pressedCursorPos = pos;
|
||||
else if (e.state == WL_POINTER_BUTTON_STATE_RELEASED && pressedCursorPos == pos)
|
||||
g_pXWaylandManager->sendCloseWindow(m_window->getGroupWindowByIndex(WINDOWINDEX));
|
||||
g_pXWaylandManager->sendCloseWindow(m_window->m_group->fromIndex(WINDOWINDEX));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -532,17 +468,17 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo
|
|||
const auto STACKPAD = *PSTACKED && (BARRELATIVEY - (m_barHeight + *POUTERGAP) * WINDOWINDEX < *POUTERGAP);
|
||||
if (TABPAD || STACKPAD) {
|
||||
if (!g_pCompositor->isWindowActive(m_window.lock()))
|
||||
Desktop::focusState()->rawWindowFocus(m_window.lock());
|
||||
Desktop::focusState()->rawWindowFocus(m_window.lock(), Desktop::FOCUS_REASON_CLICK);
|
||||
return true;
|
||||
}
|
||||
|
||||
PHLWINDOW pWindow = m_window->getGroupWindowByIndex(WINDOWINDEX);
|
||||
PHLWINDOW pWindow = m_window->m_group->fromIndex(WINDOWINDEX);
|
||||
|
||||
if (pWindow != m_window)
|
||||
pWindow->setGroupCurrent(pWindow);
|
||||
pWindow->m_group->setCurrent(pWindow);
|
||||
|
||||
if (!g_pCompositor->isWindowActive(pWindow) && *PFOLLOWMOUSE != 3)
|
||||
Desktop::focusState()->rawWindowFocus(pWindow);
|
||||
Desktop::focusState()->rawWindowFocus(pWindow, Desktop::FOCUS_REASON_CLICK);
|
||||
|
||||
if (pWindow->m_isFloating)
|
||||
g_pCompositor->changeWindowZOrder(pWindow, true);
|
||||
|
|
@ -553,13 +489,13 @@ bool CHyprGroupBarDecoration::onMouseButtonOnDeco(const Vector2D& pos, const IPo
|
|||
bool CHyprGroupBarDecoration::onScrollOnDeco(const Vector2D& pos, const IPointer::SAxisEvent e) {
|
||||
static auto PGROUPBARSCROLLING = CConfigValue<Hyprlang::INT>("group:groupbar:scrolling");
|
||||
|
||||
if (!*PGROUPBARSCROLLING || m_window->m_groupData.pNextWindow.expired())
|
||||
if (!*PGROUPBARSCROLLING || !m_window->m_group)
|
||||
return false;
|
||||
|
||||
if (e.delta > 0)
|
||||
m_window->setGroupCurrent(m_window->m_groupData.pNextWindow.lock());
|
||||
m_window->m_group->moveCurrent(true);
|
||||
else
|
||||
m_window->setGroupCurrent(m_window->getGroupPrevious());
|
||||
m_window->m_group->moveCurrent(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include "DecorationPositioner.hpp"
|
||||
#include "../../desktop/view/Window.hpp"
|
||||
#include "../../managers/HookSystemManager.hpp"
|
||||
#include "../../managers/LayoutManager.hpp"
|
||||
#include "../../layout/target/Target.hpp"
|
||||
|
||||
CDecorationPositioner::CDecorationPositioner() {
|
||||
static auto P = g_pHookSystem->hookDynamic("closeWindow", [this](void* call, SCallbackInfo& info, std::any data) {
|
||||
|
|
@ -278,7 +278,7 @@ void CDecorationPositioner::onWindowUpdate(PHLWINDOW pWindow) {
|
|||
|
||||
if (WINDOWDATA->extents != SBoxExtents{{stickyOffsetXL + reservedXL, stickyOffsetYT + reservedYT}, {stickyOffsetXR + reservedXR, stickyOffsetYB + reservedYB}}) {
|
||||
WINDOWDATA->extents = {{stickyOffsetXL + reservedXL, stickyOffsetYT + reservedYT}, {stickyOffsetXR + reservedXR, stickyOffsetYB + reservedYB}};
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow);
|
||||
pWindow->layoutTarget()->recalc();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../protocols/core/Compositor.hpp"
|
||||
#include "../../protocols/DRMSyncobj.hpp"
|
||||
#include "../../managers/input/InputManager.hpp"
|
||||
#include "../../layout/LayoutManager.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
#include <hyprutils/math/Box.hpp>
|
||||
|
|
@ -51,7 +52,7 @@ void CSurfacePassElement::draw(const CRegion& damage) {
|
|||
if (!TEXTURE->m_texID)
|
||||
return;
|
||||
|
||||
const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_pInputManager->m_currentlyDraggedWindow && g_pInputManager->m_dragMode == MBIND_RESIZE;
|
||||
const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE;
|
||||
TRACY_GPU_ZONE("RenderSurface");
|
||||
|
||||
auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface);
|
||||
|
|
@ -163,7 +164,7 @@ void CSurfacePassElement::draw(const CRegion& damage) {
|
|||
CBox CSurfacePassElement::getTexBox() {
|
||||
const double outputX = -m_data.pMonitor->m_position.x, outputY = -m_data.pMonitor->m_position.y;
|
||||
|
||||
const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_pInputManager->m_currentlyDraggedWindow && g_pInputManager->m_dragMode == MBIND_RESIZE;
|
||||
const auto INTERACTIVERESIZEINPROGRESS = m_data.pWindow && g_layoutManager->dragController()->target() && g_layoutManager->dragController()->mode() == MBIND_RESIZE;
|
||||
auto PSURFACE = Desktop::View::CWLSurface::fromResource(m_data.surface);
|
||||
|
||||
CBox windowBox;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue