[WIP PLACEHOLDER] Brainstorming tree-based window/layout handling
[07 21:37:06] although… I am not sure it's fair to call our layout nesting a hack, unless you straight up call our layouts a hack
[07 21:37:21] I mena, the layout is just a function encoded as constructors
[07 21:37:49] sublayouts are as fair game as anything else you could do that way
[07 21:42:14] I would call our layouts a hack at this point, without remorse
[07 21:42:46] especially the message handling which has absolutely no idea what windows the layout actually sees
[07 21:43:44] so if you want layout nesting as a layout, you need to store info about the windows/groups somewhere, for any action you need to send messages, and the message handler doesn't have all the info it needs
[07 21:43:48] it's a mess
[07 21:44:28] what I'd propose is a tree structure with a layout and possibly some extra extensible data at each node
[07 21:45:11] I spent some time thinking about this a couple weeks/months ago, but didn't write anything of it down :-(
[07 21:45:49] I'm fairly sure I can recall all of that should we ever seriously brainstorm this, though
[07 21:46:08] might be a good time for a wip issue then
[08 19:11:33]
root
- some-node
- floating: [windows]
- some-other-node
- floating: [windows]
What of moving floating windows from one node to another?
[08 20:14:34] doFloat function would merely send the window information up to the top branch, into the floating layer attribute.
[08 20:16:06] /**/tabbed/.
[08 20:19:08]
In case the pseudo-yaml example goes away:
"root node":
layout: sub-simple # don't subdivide rect, just stack sublayouts' wins on each other
focus: 0
children:
- "floating layer":
layout: win-floating # leaf layout, floating windows
focus: 1
children:
- Window 0x1234
- Window 0x3456
- "tiled layer":
layout: win-tall # leaf layout, tiled
focus: 0
children:
- Window 0xabcd
- Window 0x1b1b
# optionally, instead of ^
- "tiled/tabbed layer":
layout: sub-tall # tall as a super-layout
focus: 0
children:
- "tabbed pane":
layout: win-tabbed
focus: 0
children:
- Window 0x…
- Window 0x…
- "tabbed pane":
layout: win-tabbed
focus: 0
children:
- Window 0x…
- Window 0x…
My proposal:
Basic explanation, without implementation details:
root branch
- function to manage ignored windows
- ignored windows
notification
notification
- event handler functions (see below)
- focus value
- layers (cross-workspace bars, and workspaces)
- drawbuffer (xmobar/other)
- workspace
- workspace
- attributes
- name of ws
- float focus value
- float management function
- floats
- window
- sublayout (branch.general)
- window
- focus value
- layer management function
- layers
- drawbuffer (see detailed tab example)
- window
- sublayout (branch.general)
- attributes
- tabColor
- focus value
- layer management function
- layers
- drawbuffer (see detailed tab example)
- window
- sublayout
- window
- window
Detailed explanation with important implementation details:
atlas/key
- optional constant attribute
~ optional mutable attribute
> required constant attribute
< required mutable attribute
) constant
Y.Restricted.z
never gets inherited
for internal use only
Events
on
open -- when opened / initialized into the tree
close -- when closed / removed from the tree
move -- when moved in the tree - would include floating
before
open -- when opened / initialized into the tree
close -- when closed / removed from the tree
move -- when moved in the tree - would include floating
-- possibly remove this from the restricted category, if running multiseat
-- becomes feasible on Wayland
Branch.Restricted.Root
inherit Branch.General.*
-- redundant, and here for clarity. The move event would have no effect here.
inherit Events
) typeID :: Byte = IDs.Restricted.root
< ignoredHandler :: X() = notificationMover
< ignored [Branch.Window] = [ … ] -- e.g notification windows -- though, see Branch.DrawBuffer for bars
Branch.Workspace
inherit Branch.General.*
) typeID :: Byte = IDs.Restricted.workspace
< floatFocus :: Int = 0
< floatLayout :: X() = windowSnapping
< floats [Branch.*] = [ … ]
-- would also be utilized by workspaces
-- would allow a trivial workspace reorganization method
Branch.General
inherit Events
> typeID :: Byte = IDs.general -- non-constant for high extensibility
~ layout :: X() = tall -- or tiled, tabbed(full + Branch.DrawBuffer), etc
< attributes :: {*} = {
-- accessible to layout, and layers[].layout
-- maybe??: -- may become impossible to maintain
-- transparent variable inheritance and scoping
-- e.g, for informing compositors
-- if WayXMonad becomes a reality, this will be a core feature
~ opacity = 1.0
~ …
}
< focus :: Int = 0
< layers :: [Branch.*] = [ … ]
Branch.Window
inherit Events
) typeID :: Byte = IDs.Restricted.window
> id :: Int = 0x…
-- servers window properties (currently X11, future? Wayland)
< attributes :: ? = xprop
-- for things like window tagging
~ extraAttributes :: ? = { … }
Branch.DrawBuffer
inherit Events
) typeID :: Byte = IDs.Restricted.buffer
< transparent :: Boolean = True -- click-through-able
> focusable :: Boolean = False -- unable to hold focus
> method :: X() =
DrawMethods.strut -- you know
-- OR
DrawMethods.top -- always on top
-- OR
DrawMethods.bottom -- always on bottom
-- OPTIONALLY
DrawBuffer.grabWindow (\w -> w.attributes "title" == "e.g") -- ensures that target window is grabbed and held
. DrawMethods.strut -- .top or .bottom would work here too.
~ child :: Branch.* = Branch.Window 0x… -- for wrapping bars, mostly
< attributes :: {*} = { … } -- see tabs example in example tree
-- provide an automatic method for use with bars
< bounds :: Rect = [0, 0, 1920, 16]
> function :: Branch.DrawBuffer -> X() = tabDeco -- gets passed self, highly extensible, other examples: drawChild
ManageHook:
-- would have access to properties of a newly created Branch, including
-- Branch.parent.parent.parent... If so needed.
-- e.g if branch.typeID == window, branch.Events.close = shell 'echo closed window!'
Detailed example tree:
-- in runtime, not in someone's configuration, this is dynamic, of course
-- attributes not shown here, but listed above, can be inferred how you
-- please. Some of the above are examples, others defaults. Use your
-- intuition here, I'm lazy ;).
Branch.Root {
ignored = [Branch.Window 0x…, Branch.Window 0x…] -- 2 notifications are open
Events.on.close = shell "shutdown-script.sh" -- when session ends, shutdown
Events.on.open = shell "startup-apps.sh" -- when session begins, run startup apps
layers =
[ Branch.DrawBuffer { -- xmobar
transparent = False -- still usable
focusable = False -- can't hold focus
method = grabWindow (\w -> w.attributes
"title" == "xmobar") . DrawMethods.strut -- a strut is desired
bounds = [0, 0, ratio 1, auto] -- take up top x pixels of screen, xmobar decides x
function = roundCorners . drawChild -- round corners, and draw the child layer
}
, Branch.Workspace {
attributes = { name="example" }
floatLayout = snapWindows 5px
floats = [Branch.Window 0x…] -- a dialog
layout = resizeable . tall
focus = 0
layers =
[ Branch.General {
layout = tabbed
attributes = { tabHeight=16 }
focus = 2
layers =
[ Branch.DrawBuffer {
-- makes space occupied by fading tab bar is still clickable
transparent = True -- changes, managed by method()
focusable = False -- can't hold focus
method = DrawMethods.top
attributes = {
opacity = 0 -- inactive/stagnant
}
bounds = [autoCenter, 10, tabWidthDynamic, get tabHeight]
function = fadeWhenStagnant . activeEdge . roundCorners . tabs
-- ^ A theoretically beautiful implementation of
-- xmonad's tabs. Only shows up when holding mouse
-- at very top of window, or when switching between
-- windows in this sublayout. It also has rounded
-- corners. ;) (this will require a change in
-- xmonads framing code)
}
, Branch.Window 0x…
]
}
-- That was a lot of typing, I'm sure you don't want to be
-- forced to read it twice. Just know, that you could have a
-- dual subTabbed / tabbed layoutBuilder setup here. (or any
-- other sublayout, for that matter)
, anotherOneOfTheAbove
]
}
, anotherOneOfTheAbove -- you get it... workspaces, dynamic
]
}
Explanation
Rootcontains all the workspaces, logically. The entire stackset does this now, it's a good idea.Workspacesmanage their own floating windows. This does not belong inRoot. That would be unmanageable.Floatsaren't insideGeneralbecause:trackFloating- a highly desirable feature.
Generalis what each sublayout will be. It also contains attributes that most otherBranch.*children need for functioning. Every branch has a focus. This way, sublayouts with existing layouts likeaccordionmay function properly without strange jumping.Attributesare here because sublayouts may want to have some inter-op, and having semi-persistentDrawBufferinformation may be useful.DrawBufferis used fordecoration,struts, andbars. This one's probably the most extensible one here. Many existing features (listed under Why > Features) could be implemented better with this node. Using this, it's also possible to draw bounding boxes independently of any other node. Making this (haven't re-located this feature yet) possible.Window. A window, enough said.
Why do this?
- The stackset doesn't provide enough flexibility for the layouts we have currently. This has resulted in hacky solutions such as the current implementations of: sublayouts and trackFloating.
- Liskin and Geekosaur know more on this subject than I. More was discussed in the IRC snippets above.
- This allows for making things like
tabscore features, and not contrib chain-hacks. - These features will receive massive improvements in maintainability, reliability and stability:
gridSelecttreeSelectshowWNamepromptsbars
As a plus, my pseudocode is already pretty close to Haskell.
Meaning, this' suitable as a springboard template to get this thing movin'!