mirror of
https://github.com/ItsDrike/hyprland-dwindle-autogroup.git
synced 2024-11-09 18:59:40 +00:00
Merge branch 'main' into better-ungroup
This commit is contained in:
commit
e78317fdeb
9
include/func_finder.hpp
Normal file
9
include/func_finder.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "globals.hpp"
|
||||
|
||||
/* Find function pointer from the Hyprland binary by it's name and demangledName
|
||||
*
|
||||
* \param[in] name Simple name of the function (i.e. createGroup)
|
||||
* \param[in] demangledName Demangled name of the function (i.e. CWindow::createGroup())
|
||||
* \return Raw pointer to the found function
|
||||
*/
|
||||
void* findHyprlandFunction(const std::string& name, const std::string& demangledName);
|
|
@ -1,5 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <hyprland/src/layout/DwindleLayout.hpp>
|
||||
#include <hyprland/src/plugins/HookSystem.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
|
||||
const CColor s_notifyColor = {0x61 / 255.0f, 0xAF / 255.0f, 0xEF / 255.0f, 1.0f}; // RGBA
|
||||
const PLUGIN_DESCRIPTION_INFO s_pluginDescription = {"dwindle-autogroup", "Dwindle Autogroup", "ItsDrike", "1.0"};
|
||||
|
||||
inline HANDLE PHANDLE = nullptr;
|
||||
|
||||
typedef void* (*createGroupFuncT)(CWindow*);
|
||||
inline CFunctionHook* g_pCreateGroupHook = nullptr;
|
||||
|
||||
typedef void* (*destroyGroupFuncT)(CWindow*);
|
||||
inline CFunctionHook* g_pDestroyGroupHook = nullptr;
|
||||
|
||||
typedef SDwindleNodeData* (*nodeFromWindowFuncT)(void*, CWindow*);
|
||||
inline nodeFromWindowFuncT g_pNodeFromWindow = nullptr;
|
||||
|
|
9
include/plugin.hpp
Normal file
9
include/plugin.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include "globals.hpp"
|
||||
|
||||
/* New custom function replacing the original CWindow::createGroup function
|
||||
*/
|
||||
void newCreateGroup(CWindow*);
|
||||
|
||||
/* New custom function replacing the original CWindow::createGroup function
|
||||
*/
|
||||
void newDestroyGroup(CWindow*);
|
62
src/func_finder.cpp
Normal file
62
src/func_finder.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "func_finder.hpp"
|
||||
|
||||
/* Debug function for converting vector of strings to pretty-printed representation
|
||||
*
|
||||
* \param[in] Vector of strings
|
||||
* \return Pretty printed single-line representation of the vector
|
||||
*/
|
||||
std::string vectorToString(const std::vector<std::string>& vec)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[";
|
||||
|
||||
for (size_t i = 0; i < vec.size(); ++i) {
|
||||
oss << std::quoted(vec[i]);
|
||||
|
||||
// Add a comma after each element except the last one
|
||||
if (i < vec.size() - 1) {
|
||||
oss << ", ";
|
||||
}
|
||||
}
|
||||
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void* findHyprlandFunction(const std::string& name, const std::string& demangledName)
|
||||
{
|
||||
const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, name);
|
||||
|
||||
if (METHODS.size() == 0) {
|
||||
Debug::log(
|
||||
ERR, "[dwindle-autogroup] Function {} wasn't found in Hyprland binary! This function's signature probably changed, report this", name);
|
||||
HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Initialization failed! " + name + " function wasn't found", s_notifyColor, 10000);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* res = nullptr;
|
||||
for (auto& method : METHODS) {
|
||||
if (method.demangled == demangledName) {
|
||||
res = method.address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Use std::transform to extract the demangled strings from function matches
|
||||
std::vector<std::string> demangledStrings;
|
||||
std::transform(METHODS.begin(), METHODS.end(), std::back_inserter(demangledStrings), [](const SFunctionMatch& match) { return match.demangled; });
|
||||
|
||||
if (res == nullptr) {
|
||||
Debug::log(ERR,
|
||||
"[dwindle-autogroup] Demangled function {} wasn't found in matches for function name {} in Hyprland binary! This function's "
|
||||
"signature probably "
|
||||
"changed, report this. Found matches: {}",
|
||||
demangledName,
|
||||
name,
|
||||
vectorToString(demangledStrings));
|
||||
HyprlandAPI::addNotification(
|
||||
PHANDLE, "[dwindle-autogroup] Initialization failed! " + name + " function (demangled) wasn't found", s_notifyColor, 10000);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
258
src/main.cpp
258
src/main.cpp
|
@ -1,191 +1,6 @@
|
|||
#include "func_finder.hpp"
|
||||
#include "globals.hpp"
|
||||
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/Window.hpp>
|
||||
#include <hyprland/src/layout/DwindleLayout.hpp>
|
||||
#include <hyprland/src/layout/IHyprLayout.hpp>
|
||||
#include <hyprland/src/managers/KeybindManager.hpp>
|
||||
#include <hyprland/src/managers/LayoutManager.hpp>
|
||||
#include <hyprland/src/render/decorations/CHyprGroupBarDecoration.hpp>
|
||||
|
||||
const CColor s_pluginColor = {0x61 / 255.0f, 0xAF / 255.0f, 0xEF / 255.0f, 1.0f};
|
||||
|
||||
inline std::function<void(std::string)> originalToggleGroup = nullptr;
|
||||
|
||||
typedef SDwindleNodeData* (*nodeFromWindowT)(void*, CWindow*);
|
||||
inline nodeFromWindowT g_pNodeFromWindow = nullptr;
|
||||
|
||||
void collectChildNodes(std::deque<SDwindleNodeData*>* pDeque, SDwindleNodeData* node)
|
||||
{
|
||||
if (node->isNode) {
|
||||
collectChildNodes(pDeque, node->children[0]);
|
||||
collectChildNodes(pDeque, node->children[1]);
|
||||
}
|
||||
else {
|
||||
pDeque->emplace_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is partially from CKeybindManager::moveIntoGroup (dispatcher) function
|
||||
/// but without making the new window focused.
|
||||
void moveIntoGroup(CWindow* window, CWindow* groupRootWin)
|
||||
{
|
||||
// Remove this window from being shown by layout (it will become a part of a group)
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(window);
|
||||
|
||||
// Create a group bar decoration for the window
|
||||
// (if it's not already a group, in which case it should already have it)
|
||||
if (!window->m_sGroupData.pNextWindow)
|
||||
window->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(window));
|
||||
|
||||
groupRootWin->insertWindowToGroup(window);
|
||||
|
||||
// Make sure to treat this window as hidden (will focus the group instead of this window
|
||||
// on request activate). This is the behavior CWindow::setGroupCurrent uses.
|
||||
window->setHidden(true);
|
||||
}
|
||||
|
||||
void collectGroupWindows(std::deque<CWindow*>* pDeque, CWindow* pWindow)
|
||||
{
|
||||
CWindow* curr = pWindow;
|
||||
do {
|
||||
const auto PLAST = curr;
|
||||
pDeque->emplace_back(curr);
|
||||
curr = curr->m_sGroupData.pNextWindow;
|
||||
} while (curr != pWindow);
|
||||
}
|
||||
|
||||
void groupDissolve(const SDwindleNodeData* PNODE, CHyprDwindleLayout* layout)
|
||||
{
|
||||
CWindow* PWINDOW = PNODE->pWindow;
|
||||
|
||||
// This group only has this single winodw
|
||||
if (PWINDOW->m_sGroupData.pNextWindow == PWINDOW) {
|
||||
Debug::log(LOG, "Ignoring autogroup on single window dissolve");
|
||||
originalToggleGroup("");
|
||||
return;
|
||||
}
|
||||
|
||||
// We could just call originalToggleGroup function here, but that fucntion doesn't
|
||||
// respect the dwindle layout, and would just place the newly ungroupped windows
|
||||
// randomly throughout the worskapce, messing up the layout. So instead, we replicate
|
||||
// it's behavior here manually, taking care to disolve the groups nicely.
|
||||
Debug::log(LOG, "Dissolving group");
|
||||
|
||||
std::deque<CWindow*> members;
|
||||
collectGroupWindows(&members, PWINDOW);
|
||||
|
||||
// If the group head window is in fullscreen, unfullscreen it.
|
||||
// We need to have the window placed in the layout, to figure out where
|
||||
// to ungroup the rest of the windows.
|
||||
g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL);
|
||||
|
||||
Debug::log(LOG, "Ungroupping Members");
|
||||
|
||||
const bool GROUPSLOCKEDPREV = g_pKeybindManager->m_bGroupsLocked;
|
||||
|
||||
for (auto& w : members) {
|
||||
w->m_sGroupData.pNextWindow = nullptr;
|
||||
w->setHidden(false);
|
||||
|
||||
// Ask layout to create a new window for all windows that were in the group
|
||||
// except for the group head (already has a window).
|
||||
if (w->m_sGroupData.head) {
|
||||
Debug::log(LOG, "Ungroupping member head window");
|
||||
w->m_sGroupData.head = false;
|
||||
|
||||
// Update the window decorations (removing group bar)
|
||||
// w->updateWindowDecos();
|
||||
}
|
||||
else {
|
||||
Debug::log(LOG, "Ungroupping member non-head window");
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreatedTiling(w);
|
||||
// Focus the window that we just spawned, so that on the next iteration
|
||||
// the window created will be it's dwindle child node.
|
||||
// This allows the original group head to remain a parent window to all
|
||||
// of the other (groupped) nodes
|
||||
g_pCompositor->focusWindow(w);
|
||||
}
|
||||
}
|
||||
|
||||
g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV;
|
||||
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
// Leave with the focus the original group (head) window
|
||||
g_pCompositor->focusWindow(PWINDOW);
|
||||
}
|
||||
|
||||
void groupCreate(const SDwindleNodeData* PNODE, CHyprDwindleLayout* layout)
|
||||
{
|
||||
const auto PWINDOW = PNODE->pWindow;
|
||||
|
||||
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PNODE->workspaceID);
|
||||
if (PWORKSPACE->m_bHasFullscreenWindow) {
|
||||
Debug::log(LOG, "Ignoring autogroup on a fullscreen window");
|
||||
originalToggleGroup("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PNODE->pParent) {
|
||||
Debug::log(LOG, "Ignoring autogroup for a solitary window");
|
||||
originalToggleGroup("");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Creating group");
|
||||
// Call the original toggleGroup function, and only do extra things afterwards
|
||||
originalToggleGroup("");
|
||||
|
||||
std::deque<SDwindleNodeData*> newGroupMembers;
|
||||
collectChildNodes(&newGroupMembers, PNODE->pParent->children[0] == PNODE ? PNODE->pParent->children[1] : PNODE->pParent->children[0]);
|
||||
|
||||
// Make sure one of the child nodes isn't itself a group
|
||||
for (auto& n : newGroupMembers) {
|
||||
if (n->pWindow->m_sGroupData.pNextWindow) {
|
||||
Debug::log(LOG, "Ignoring autogroup for nested groups");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the children nodes into the group
|
||||
for (auto& n : newGroupMembers) {
|
||||
auto window = n->pWindow;
|
||||
moveIntoGroup(window, PWINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
void toggleGroup(std::string args)
|
||||
{
|
||||
// We only care about group creations, not group disolves
|
||||
const auto PWINDOW = g_pCompositor->m_pLastWindow;
|
||||
if (!PWINDOW || !g_pCompositor->windowValidMapped(PWINDOW))
|
||||
return;
|
||||
|
||||
// Don't do anything if we're not on "dwindle" layout
|
||||
IHyprLayout* layout = g_pLayoutManager->getCurrentLayout();
|
||||
if (layout->getLayoutName() != "dwindle") {
|
||||
Debug::log(LOG, "Ignoring autogroup for non-dinwle layout");
|
||||
originalToggleGroup(args);
|
||||
return;
|
||||
}
|
||||
|
||||
CHyprDwindleLayout* cur_dwindle = (CHyprDwindleLayout*)layout;
|
||||
|
||||
const auto PNODE = g_pNodeFromWindow(cur_dwindle, PWINDOW);
|
||||
if (!PNODE) {
|
||||
Debug::log(LOG, "Ignoring autogroup for floating window");
|
||||
originalToggleGroup(args);
|
||||
return;
|
||||
}
|
||||
|
||||
if (PWINDOW->m_sGroupData.pNextWindow) {
|
||||
groupDissolve(PNODE, cur_dwindle);
|
||||
}
|
||||
else {
|
||||
groupCreate(PNODE, cur_dwindle);
|
||||
}
|
||||
}
|
||||
#include "plugin.hpp"
|
||||
|
||||
// Do NOT change this function.
|
||||
APICALL EXPORT std::string PLUGIN_API_VERSION()
|
||||
|
@ -193,44 +8,53 @@ APICALL EXPORT std::string PLUGIN_API_VERSION()
|
|||
return HYPRLAND_API_VERSION;
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT()
|
||||
{
|
||||
// Unhook the overridden functions and remove the hooks
|
||||
if (g_pCreateGroupHook) {
|
||||
g_pCreateGroupHook->unhook();
|
||||
HyprlandAPI::removeFunctionHook(PHANDLE, g_pCreateGroupHook);
|
||||
g_pCreateGroupHook = nullptr;
|
||||
}
|
||||
if (g_pDestroyGroupHook) {
|
||||
g_pDestroyGroupHook->unhook();
|
||||
HyprlandAPI::removeFunctionHook(PHANDLE, g_pDestroyGroupHook);
|
||||
g_pCreateGroupHook = nullptr;
|
||||
}
|
||||
|
||||
// Plugin unloading was successful
|
||||
HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Unloaded successfully!", s_notifyColor, 5000);
|
||||
}
|
||||
|
||||
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle)
|
||||
{
|
||||
PHANDLE = handle;
|
||||
|
||||
// Get address of the private CHyprDwindleLayout::getNodeFromWindow member function, we'll need it in toggleGroup
|
||||
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "getNodeFromWindow");
|
||||
g_pNodeFromWindow = (nodeFromWindowT)METHODS[1].address;
|
||||
Debug::log(LOG, "[dwindle-autogroup] Loading Hyprland functions");
|
||||
|
||||
for (auto& method : METHODS) {
|
||||
if (method.demangled == "CHyprDwindleLayout::getNodeFromWindow(CWindow*)") {
|
||||
g_pNodeFromWindow = (nodeFromWindowT)method.address;
|
||||
break;
|
||||
}
|
||||
// Find pointers to functions by name (from the Hyprland binary)
|
||||
g_pNodeFromWindow = (nodeFromWindowFuncT)findHyprlandFunction("getNodeFromWindow", "\nCHyprDwindleLayout::getNodeFromWindow(CWindow*)");
|
||||
auto pCreateGroup = findHyprlandFunction("createGroup", "\nCWindow::createGroup()");
|
||||
auto pDestroyGroup = findHyprlandFunction("destroyGroup", "\nCWindow::destroyGroup()");
|
||||
|
||||
// Return immediately if one of the functions wasn't found
|
||||
if (!g_pNodeFromWindow || !pCreateGroup || !pDestroyGroup) {
|
||||
// Set all of the global function pointers to NULL, to avoid any potential issues
|
||||
g_pNodeFromWindow = nullptr;
|
||||
|
||||
return s_pluginDescription;
|
||||
}
|
||||
|
||||
if (g_pNodeFromWindow == nullptr) {
|
||||
Debug::log(ERR, "getNodeFromWindow funnction for dwindle layout wasn't found! This function's signature probably changed, report this");
|
||||
HyprlandAPI::addNotification(
|
||||
PHANDLE, "[dwindle-autogroup] Initialization failed!! getNodeFromWindow functio not found", s_pluginColor, 10000);
|
||||
}
|
||||
else {
|
||||
originalToggleGroup = g_pKeybindManager->m_mDispatchers["togglegroup"];
|
||||
HyprlandAPI::addDispatcher(PHANDLE, "togglegroup", toggleGroup);
|
||||
Debug::log(LOG, "[dwindle-autogroup] Registering function hooks");
|
||||
|
||||
HyprlandAPI::reloadConfig();
|
||||
// Register function hooks, for overriding the original group methods
|
||||
g_pCreateGroupHook = HyprlandAPI::createFunctionHook(PHANDLE, pCreateGroup, (void*)&newCreateGroup);
|
||||
g_pDestroyGroupHook = HyprlandAPI::createFunctionHook(PHANDLE, pDestroyGroup, (void*)&newDestroyGroup);
|
||||
|
||||
HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Initialized successfully!", s_pluginColor, 5000);
|
||||
}
|
||||
// Initialize the hooks, from now on, the original functions will be overridden
|
||||
g_pCreateGroupHook->hook();
|
||||
g_pDestroyGroupHook->hook();
|
||||
|
||||
return {"dwindle-autogroup", "Dwindle Autogroup", "ItsDrike", "1.0"};
|
||||
}
|
||||
|
||||
APICALL EXPORT void PLUGIN_EXIT()
|
||||
{
|
||||
// Since we added the "togglegroup" dispatcher ourselves, by default, the cleanup would just remove it
|
||||
// but we want to restore it back to the original function instead
|
||||
HyprlandAPI::removeDispatcher(PHANDLE, "togglegroup");
|
||||
g_pKeybindManager->m_mDispatchers["togglegroup"] = originalToggleGroup;
|
||||
|
||||
HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Unloaded successfully!", s_pluginColor, 5000);
|
||||
HyprlandAPI::addNotification(PHANDLE, "[dwindle-autogroup] Initialized successfully!", s_notifyColor, 5000);
|
||||
return s_pluginDescription;
|
||||
}
|
||||
|
|
226
src/plugin.cpp
Normal file
226
src/plugin.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
#include "plugin.hpp"
|
||||
#include "globals.hpp"
|
||||
#include <hyprland/src/Compositor.hpp>
|
||||
#include <hyprland/src/layout/DwindleLayout.hpp>
|
||||
#include <hyprland/src/managers/LayoutManager.hpp>
|
||||
#include <hyprland/src/plugins/PluginAPI.hpp>
|
||||
#include <hyprland/src/render/decorations/CHyprGroupBarDecoration.hpp>
|
||||
|
||||
/*! Recursively collect all dwindle child nodes for given root node
|
||||
*
|
||||
* \param[out] pDeque deque to store the found child nodes into
|
||||
* \param[in] pNode Dwindle node which children should be collected
|
||||
*/
|
||||
void collectDwindleChildNodes(std::deque<SDwindleNodeData*>* pDeque, SDwindleNodeData* pNode)
|
||||
{
|
||||
if (pNode->isNode) {
|
||||
collectDwindleChildNodes(pDeque, pNode->children[0]);
|
||||
collectDwindleChildNodes(pDeque, pNode->children[1]);
|
||||
}
|
||||
else {
|
||||
pDeque->emplace_back(pNode);
|
||||
}
|
||||
}
|
||||
|
||||
/*! Collect all windows that belong to the same group
|
||||
*
|
||||
* \param[out] pDeque deque to store the found group windows into
|
||||
* \param[in] pWindow any window that belongs to a group (doesn't have to be the group head window)
|
||||
*/
|
||||
void collectGroupWindows(std::deque<CWindow*>* pDeque, CWindow* pWindow)
|
||||
{
|
||||
CWindow* curr = pWindow;
|
||||
do {
|
||||
const auto PLAST = curr;
|
||||
pDeque->emplace_back(curr);
|
||||
curr = curr->m_sGroupData.pNextWindow;
|
||||
} while (curr != pWindow);
|
||||
}
|
||||
|
||||
/*! Move given window into a group
|
||||
*
|
||||
* This is almost the same as CKeybindManager::moveWindowIntoGroup (dispatcher) function,
|
||||
* but without making the new window a group head and focused.
|
||||
*
|
||||
* \param[in] pWindow Window to be inserted into a group
|
||||
* \param[in] pGroupWindow Window that's a part of a group to insert the pWindow into
|
||||
*/
|
||||
void moveIntoGroup(CWindow* pWindow, CWindow* pGroupHeadWindow)
|
||||
{
|
||||
const auto P_LAYOUT = g_pLayoutManager->getCurrentLayout();
|
||||
|
||||
const auto* USE_CURR_POS = &g_pConfigManager->getConfigValuePtr("misc:group_insert_after_current")->intValue;
|
||||
CWindow* pGroupWindow = *USE_CURR_POS ? pGroupHeadWindow : pGroupHeadWindow->getGroupTail();
|
||||
|
||||
// Remove the window from layout (will become a part of a group)
|
||||
P_LAYOUT->onWindowRemoved(pWindow);
|
||||
|
||||
// Create a group bar decoration for the window we'll be inserting
|
||||
// (if it's not already a group, in which case it should already have it)
|
||||
if (!pWindow->m_sGroupData.pNextWindow)
|
||||
pWindow->m_dWindowDecorations.emplace_back(std::make_unique<CHyprGroupBarDecoration>(pWindow));
|
||||
|
||||
pGroupWindow->insertWindowToGroup(pWindow);
|
||||
pGroupHeadWindow->updateWindowDecos();
|
||||
pWindow->setHidden(true);
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->recalculateWindow(pGroupHeadWindow);
|
||||
}
|
||||
|
||||
/*! Check common pre-conditions for group creation/deletion and perform needed initializations
|
||||
*
|
||||
* \param[out] pDwindleLayout Pointer to dwindle layout instance
|
||||
* \return Necessary pre-conditions succeded?
|
||||
*/
|
||||
bool handleGroupOperation(CHyprDwindleLayout** pDwindleLayout)
|
||||
{
|
||||
const auto P_LAYOUT = g_pLayoutManager->getCurrentLayout();
|
||||
if (P_LAYOUT->getLayoutName() != "dwindle") {
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ignoring non-dwindle layout");
|
||||
return false;
|
||||
}
|
||||
|
||||
*pDwindleLayout = dynamic_cast<CHyprDwindleLayout*>(P_LAYOUT);
|
||||
return true;
|
||||
}
|
||||
|
||||
void newCreateGroup(CWindow* self)
|
||||
{
|
||||
// Run the original function first, creating a "classical" group
|
||||
// with just the currently selected window in it
|
||||
((createGroupFuncT)g_pCreateGroupHook->m_pOriginal)(self);
|
||||
|
||||
// Only continue if the group really was created, as there are some pre-conditions to that.
|
||||
if (!self->m_sGroupData.pNextWindow) {
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ignoring autogroup - invalid / non-group widnow");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Triggered createGroup for {:x}", self);
|
||||
|
||||
// Obtain an instance of the dwindle layout, also run some general pre-conditions
|
||||
// for the plugin, quit now if they're not met.
|
||||
CHyprDwindleLayout* pDwindleLayout = nullptr;
|
||||
if (!handleGroupOperation(&pDwindleLayout))
|
||||
return;
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Autogroupping dwindle child nodes");
|
||||
|
||||
// Collect all child dwindle nodes, we'll want to add all of those into a group
|
||||
const auto P_DWINDLE_NODE = g_pNodeFromWindow(pDwindleLayout, self);
|
||||
std::deque<SDwindleNodeData*> p_dDwindleNodes;
|
||||
const auto P_SIBLING_NODE =
|
||||
P_DWINDLE_NODE->pParent->children[0] == P_DWINDLE_NODE ? P_DWINDLE_NODE->pParent->children[1] : P_DWINDLE_NODE->pParent->children[0];
|
||||
collectDwindleChildNodes(&p_dDwindleNodes, P_SIBLING_NODE);
|
||||
|
||||
// Stop if one of the dwindle child nodes is already in a group
|
||||
for (auto& node : p_dDwindleNodes) {
|
||||
auto curWindow = node->pWindow;
|
||||
if (curWindow->m_sGroupData.pNextWindow) {
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ignoring autogroup for nested groups: window {:x} is group", curWindow);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Found {} dwindle nodes to autogroup", p_dDwindleNodes.size());
|
||||
|
||||
// Add all of the dwindle child node widnows into the group
|
||||
for (auto& node : p_dDwindleNodes) {
|
||||
auto curWindow = node->pWindow;
|
||||
Debug::log(LOG, "[dwindle-autogroup] Moving window {:x} into group", curWindow);
|
||||
moveIntoGroup(curWindow, self);
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Autogroup done, {} child nodes moved", p_dDwindleNodes.size());
|
||||
}
|
||||
|
||||
void newDestroyGroup(CWindow* self)
|
||||
{
|
||||
// We can't use the original function here (other than for falling back)
|
||||
// as it removes the group head and then creates the new windows on the
|
||||
// layout. This often messes up the user layout of the windows.
|
||||
//
|
||||
// The goal of this function is to ungroup the windows such that they
|
||||
// only continue as children from the dwindle binary tree node the group
|
||||
// head was on.
|
||||
|
||||
// Only continue if the window isn't in a group
|
||||
if (!self->m_sGroupData.pNextWindow) {
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ignoring ungroup - invalid / non-group widnow");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Triggered destroyGroup for {:x}", self);
|
||||
|
||||
// Obtain an instance of the dwindle layout, also run some general pre-conditions
|
||||
// for the plugin, fall back now if they're not met.
|
||||
CHyprDwindleLayout* pDwindleLayout = nullptr;
|
||||
if (!handleGroupOperation(&pDwindleLayout)) {
|
||||
((createGroupFuncT)g_pDestroyGroupHook->m_pOriginal)(self);
|
||||
return;
|
||||
}
|
||||
|
||||
std::deque<CWindow*> dGroupWindows;
|
||||
collectGroupWindows(&dGroupWindows, self);
|
||||
|
||||
// If the group head window is in fullscreen, unfullscreen it.
|
||||
// We need to have the window placed in the layout, to figure out where
|
||||
// to ungroup the rest of the windows.
|
||||
g_pCompositor->setWindowFullscreen(self, false, FULLSCREEN_FULL);
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ungroupping {} windows", dGroupWindows.size());
|
||||
|
||||
const bool GROUPS_LOCKED_PREV = g_pKeybindManager->m_bGroupsLocked;
|
||||
g_pKeybindManager->m_bGroupsLocked = true;
|
||||
|
||||
for (auto& pWindow : dGroupWindows) {
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ungroupping window {:x}", pWindow);
|
||||
pWindow->m_sGroupData.pNextWindow = nullptr;
|
||||
pWindow->m_sGroupData.head = false;
|
||||
|
||||
// Current / Visible window (this isn't always the head)
|
||||
if (!pWindow->isHidden()) {
|
||||
Debug::log(LOG, "[dwindle-autogroup] -> Visible window ungroup");
|
||||
|
||||
// This window is already visible in the layout, we don't need to create
|
||||
// a new layout window for it.
|
||||
//
|
||||
// The original destroyGroup removes the window from the layout here,
|
||||
// which is what causes the weird ungroupping behavior as this window
|
||||
// is then recreated, which spawns it in a potentially unexpected place
|
||||
// (often determined by the cursor position).
|
||||
|
||||
// Update the window decorations (removing group bar)
|
||||
pWindow->updateWindowDecos();
|
||||
}
|
||||
else {
|
||||
pWindow->setHidden(false);
|
||||
|
||||
g_pLayoutManager->getCurrentLayout()->onWindowCreatedTiling(pWindow);
|
||||
pWindow->updateWindowDecos();
|
||||
|
||||
// Focus the window that we just spawned, so that on the next iteration
|
||||
// the window created will be it's dwindle child node.
|
||||
// This allows the original group head to remain a parent window to all
|
||||
// of the other (groupped) nodes.
|
||||
//
|
||||
// Note that this won't preserve the exact original layout of the group
|
||||
// but it will make sure all of the groupped windows will extend from
|
||||
// the dwindle node of the group head window. Preserving the original
|
||||
// layout isn't really possible, since new windows can be added into
|
||||
// groups after they were created.
|
||||
g_pCompositor->focusWindow(pWindow);
|
||||
}
|
||||
}
|
||||
|
||||
g_pKeybindManager->m_bGroupsLocked = GROUPS_LOCKED_PREV;
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] All windows ungroupped");
|
||||
|
||||
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
|
||||
|
||||
// Leave with the focus the original (main) window
|
||||
g_pCompositor->focusWindow(self);
|
||||
|
||||
Debug::log(LOG, "[dwindle-autogroup] Ungroupping done");
|
||||
}
|
Loading…
Reference in a new issue