Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 97 additions & 20 deletions src/managers/MasterStackLayoutManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
KEY_MASTER_WIDTH = "masterWidth"
KEY_STACK_LAYOUT = "stackLayout"
KEY_STACK_SIDE = "stackSide"
KEY_DEPTH_LIMIT = "depthLimit"


class MasterStackLayoutManager(WorkspaceLayoutManager):
Expand All @@ -36,6 +37,7 @@ def __init__(self, con, workspace, options):
self.masterWidth = options.getForWorkspace(self.workspaceNum, KEY_MASTER_WIDTH) or 50
self.stackLayout = options.getForWorkspace(self.workspaceNum, KEY_STACK_LAYOUT) or "splitv"
self.stackSide = options.getForWorkspace(self.workspaceNum, KEY_STACK_SIDE) or "right"
self.depthLimit = options.getForWorkspace(self.workspaceNum, KEY_DEPTH_LIMIT) or 0

# If windows exist, fit them into MasterStack
self.arrangeUntrackedWindows()
Expand Down Expand Up @@ -79,19 +81,47 @@ def windowFocused(self, event, window):


def onBinding(self, command):
if command == "move up":
if command == "nop layman move up":
self.moveUp()
elif command == "move down":
elif command == "nop layman move down":
self.moveDown()
elif command == "rotate ccw" or command == "move left":
elif command == "nop layman move right":
focusedWindow = self.getFocusedCon()
if not focusedWindow:
return
# swap with master if direction is correct
if (focusedWindow.id == self.masterId and self.stackSide == "right"
or (focusedWindow.id != self.masterId and self.stackSide == "left"
and self.stackLayout == "splitv")):
self.swapMaster()
elif self.stackLayout == "splith" and focusedWindow.id != self.masterId:
if self.stackSide == "left":
self.moveUp()
elif self.stackSide == "right":
self.moveDown()
elif command == "nop layman move left":
focusedWindow = self.getFocusedCon()
if not focusedWindow:
return
# swap with master if direction is correct
if (focusedWindow.id == self.masterId and self.stackSide == "left"
or (focusedWindow.id != self.masterId and self.stackSide == "right"
and self.stackLayout == "splitv")):
self.swapMaster()
elif self.stackLayout == "splith" and focusedWindow.id != self.masterId:
if self.stackSide == "left":
self.moveDown()
elif self.stackSide == "right":
self.moveUp()
elif command == "nop layman rotate ccw":
self.rotateCCW()
elif command == "rotate cw" or command == "move right":
elif command == "nop layman rotate cw":
self.rotateCW()
elif command == "swap master":
elif (command == "nop layman swap master"):
self.swapMaster()
elif command == "stack toggle":
elif command == "nop layman stack toggle":
self.toggleStackLayout()
elif command == "stackside toggle":
elif command == "nop layman stackside toggle":
self.toggleStackSide()


Expand Down Expand Up @@ -143,7 +173,7 @@ def arrangeUntrackedWindows(self):
untracked = [x for x in reversed(leaves) if x.id not in self.stack and x.id != self.masterId]
for window in untracked:
if self.stackId == 0:
if self.masterId == 0:
if not self.getConById(self.masterId) or self.masterId == 0:
self.initMaster(window)
else:
self.initStack(window)
Expand Down Expand Up @@ -174,6 +204,7 @@ def pushMasterToStack(self, window):
self.moveWindow(self.masterId, self.stackId)
self.moveToTopOfStack(self.masterId)
self.masterId = window.id
self.updateSubStack()


def pushWindow(self, window, topCon):
Expand Down Expand Up @@ -207,9 +238,11 @@ def moveToTopOfStack(self, windowId):
# The top of a tabbed layout is the closest to master, handle that
moveDirection = "up"
topIndex = 0
if self.stackLayout == "tabbed" and self.stackSide == "right":
if ((self.stackLayout == "tabbed" and self.stackSide == "right")
or self.stackLayout == "splith" and self.stackSide == "right"):
moveDirection = "left"
elif self.stackLayout == "tabbed" and self.stackSide == "left":
elif ((self.stackLayout == "tabbed" and self.stackSide == "left")
or self.stackLayout == "splith" and self.stackSide == "left"):
moveDirection = "right"
topIndex = -1

Expand All @@ -228,7 +261,37 @@ def moveToTopOfStack(self, windowId):
if stackCon.id != self.stackId:
self.moveWindow(windowId, self.stackId)
stackCon = self.getConById(self.stackId)


def updateSubStack(self):
if (self.stackLayout != "splitv"and self.stackLayout != "splith"):
return
moveDirection = "down"
moveRedirection = "up"
if self.stackLayout == "splith":
moveDirection = self.stackSide
moveRedirection = "right" if self.stackSide == "left" else "left"
lastCon = self.getConById(self.stack[0]).parent
focusedWindow = self.getFocusedCon()
if lastCon.id == self.stackId and len(self.stack) > self.depthLimit and self.depthLimit >= 1:
# if not yet created and depthLimit reached, create second stack
self.con.command("[con_id=%d] split vertical, layout stacking" % self.stack[0])
self.con.command("[con_id=%d] move %s" % (self.stack[1], moveDirection))
elif lastCon.id != self.stackId:
numLast = len(lastCon.leaves())
numFirst = len(self.stack) - numLast
if numFirst > (self.depthLimit - 1):
# if first stack number is higher than the maximum allowed, put lowest one in second stack
self.con.command("[con_id=%d] move %s" % (self.stack[numLast], moveDirection))
elif numFirst < (self.depthLimit - 1):
# if first stack number is lower than the maximum allowed, pop one from second stack
self.con.command("[con_id=%d] move %s" % (self.stack[numLast - 1], moveRedirection))
if numLast == 2:
# Remove also last window from SubStack if only two windows were in it
self.con.command("[con_id=%d] move %s" % (self.stack[0], moveRedirection))
elif numLast == 1:
self.con.command("[con_id=%d] move %s" % (self.stack[0], moveRedirection))

self.con.command("[con_id=%d] focus" % focusedWindow.id)

def popFromStack(self, windowId, leaves):
# Master destroyed, pop from stack
Expand Down Expand Up @@ -274,6 +337,8 @@ def popWindow(self, window, topCon):
# Master destroyed, pop from stack
newMaster = self.stack.pop()
self.popFromStack(newMaster, leaves)
if len(self.stack) > 0:
self.updateSubStack()
elif len(topCon.nodes) == 1 and len(leaves) > 1:
# Layout is wrapped in another container, recurse
self.popWindow(window, topCon.nodes[0])
Expand All @@ -284,6 +349,7 @@ def popWindow(self, window, topCon):
for id in self.stack:
if id not in allWindowIds:
self.stack.remove(id)
self.updateSubStack()
break


Expand Down Expand Up @@ -354,7 +420,7 @@ def moveUp(self):


def moveDown(self):
# Check if stack only has one window
# Check if stack only has one window
if len(self.stack) < 2:
return

Expand All @@ -370,10 +436,11 @@ def moveDown(self):

# Swap with top of stack if master is focused
if focusedWindow.id == self.masterId:
self.con.command("[con_id=%d] swap container with con_id %d" % (focusedWindow.id, self.stack[-1]))
self.masterId = self.stack.pop()
self.stack.append(focusedWindow.id)
self.log("Swapped master %d with top of stack %d" % (self.stack[-1], self.masterId))
targetId = self.stack.pop()
self.con.command("[con_id=%d] swap container with con_id %d" % (targetId, self.masterId))
self.stack.append(self.masterId)
self.masterId = targetId
self.log("Swapped window %d with master" % targetId)
return

# Swap window with window below
Expand All @@ -390,9 +457,13 @@ def moveDown(self):


def rotateCCW(self):
# Exit if less than three windows
# Swap master and top of stack if only two windows
if len(self.stack) < 2:
self.log("Only 2 windows, can't rotate")
targetId = self.stack.pop()
self.con.command("[con_id=%d] swap container with con_id %d" % (targetId, self.masterId))
self.stack.append(self.masterId)
self.masterId = targetId
self.log("Swapped window %d with master" % targetId)
return

# Swap top of stack with master, then move old master to bottom
Expand All @@ -408,12 +479,17 @@ def rotateCCW(self):
# Update record
self.masterId = newMasterId
self.stack.appendleft(prevMasterId)
self.updateSubStack()


def rotateCW(self):
# Exit if less than three windows
# Swap master and top of stack if only two windows
if len(self.stack) < 2:
self.log("Only 2 windows, can't rotate")
targetId = self.stack.pop()
self.con.command("[con_id=%d] swap container with con_id %d" % (targetId, self.masterId))
self.stack.append(self.masterId)
self.masterId = targetId
self.log("Swapped window %d with master" % targetId)
return

# Swap bottom of stack with master, then move old master to top
Expand All @@ -434,6 +510,7 @@ def rotateCW(self):
# Update record
self.masterId = newMasterId
self.stack.append(prevMasterId)
self.updateSubStack()


def swapMaster(self):
Expand Down