├── layout_icon.png ├── screenshot.png ├── README.md ├── bintree.lua └── init.lua /layout_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guotsuan/awesome-treetile/HEAD/layout_icon.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guotsuan/awesome-treetile/HEAD/screenshot.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Treetile 2 | ======== 3 | 4 | *Codes work with git version of awesome and sould work with stable version too (e.g. 3.5.9 currently). Let me know if they fail.* 5 | 6 | Treetile is binary tree-based, dynamical tiling layout for Awesome 3.5 and 7 | latter. Similarly to tmux or i3wm, if a new client/window is created, 8 | the screen area occupied by the previous focused client (progenitor) will be 9 | split vertically or horizontally and shared equally by the new and the previous 10 | focused client (descendants). Each time the spilt can either be specified or depends on 11 | which side (width or height) of the screen area of the previous focused client (progenitor) 12 | is longer. If you want, you can also manually resize these two descendants with 13 | respect to each other by the keyboad or mouse, but only in the frame of screen area occupied by the 14 | progenitor (which can be improved in the future). 15 | 16 | This project is forked from (https://github.com/RobSis/treesome) and still under the development. 17 | Comments and feedbacks are welcome. 18 | 19 | 20 | Installation 21 | --- 22 | 23 | 1. Clone repository to your awesome directory 24 | 25 | ``` 26 | git clone http://github.com/guotsuan/treetile.git ~/.config/awesome/treetile 27 | ``` 28 | 29 | 2. Add this line to your rc.lua below other require calls. 30 | 31 | ```lua 32 | local treetile = require("treetile")` 33 | ``` 34 | 35 | 3. And finally add the layout `treetile` to your layout table. 36 | ```lua 37 | local layouts = { 38 | ... 39 | treetile 40 | } 41 | ``` 42 | 4. ##### Important Option: 43 | if you set the in your `rc.lua` to let the new created client gain the focus, 44 | for example: 45 | ```lua 46 | ... 47 | { rule = { }, 48 | properties = { focus = awful.client.focus.filter, 49 | -- or focus = true, 50 | 51 | ... 52 | ``` 53 | 54 | then you should set the following option to make sure treetile works correctly 55 | ```lua 56 | treetile.focusnew = true 57 | ``` 58 | If no extra settings about focus are added in your rc.lua, please set 59 | ```lua 60 | treetile.focusnew = false 61 | ``` 62 | 5. Restart and you're done. 63 | 64 | 65 | Configuration 66 | ---- 67 | 68 | 1. The following option controls the new client apprear on the left or the right side 69 | of current client: 70 | ```lua 71 | treetile.direction = "right" -- or "left" 72 | ``` 73 | 74 | 2. By default, direction of split is decided based on the dimensions of the last focused 75 | client. If you want you to force the direction of the split, bind keys to 76 | `treetile.vertical` and `treetile.horizontal` functions. For example: 77 | ```lua 78 | awful.key({ modkey }, "v", treetile.vertical), 79 | awful.key({ modkey }, "h", treetile.horizontal) 80 | ``` 81 | 82 | 3. Set the keyboad shortcut for resizing the descendant clients 83 | ` treetile.resize_client(inc) `. The value of inc can be from 0.01 to 0.99, 84 | negative or postive, for example: 85 | ```lua 86 | ... 87 | awful.key({ modkey, "Shift" }, "h", function () 88 | local c = client.focus 89 | if awful.layout.get(c.screen).name ~= "treetile" then 90 | awful.client.moveresize(-20,0,0,0) 91 | else 92 | treetile.resize_client(-0.1) 93 | -- increase or decrease by percentage of current width or height, 94 | -- the value can be from 0.01 to 0.99, negative or postive 95 | end 96 | end), 97 | awful.key({ modkey, "Shift" }, "l", function () 98 | local c = client.focus 99 | if awful.layout.get(c.screen).name ~= "treetile" then 100 | awful.client.moveresize(20,0,0,0) 101 | else 102 | treetile.resize_client(0.1) 103 | end 104 | end), 105 | ... 106 | ``` 107 | 108 | Screenshots 109 | ----------- 110 | 111 | ![screenshot](./screenshot.png) 112 | 113 | TODO 114 | ---------- 115 | 1. The resizing of clients can be improved 116 | 117 | 118 | Licence 119 | ------- 120 | 121 | [GPL 2.0](http://www.gnu.org/licenses/gpl-2.0.html) 122 | -------------------------------------------------------------------------------- /bintree.lua: -------------------------------------------------------------------------------- 1 | -- bintree.lua 2 | -- Class representing the binary tree 3 | local Bintree = {} 4 | Bintree.__index = Bintree 5 | 6 | 7 | function Bintree.new(data, left, right) 8 | local node = { 9 | data = data, 10 | left = left, 11 | right = right, 12 | } 13 | return setmetatable(node,Bintree) 14 | end 15 | 16 | function Bintree:addLeft(child) 17 | if self ~= nil then 18 | self.left = child 19 | return self.left 20 | end 21 | end 22 | 23 | function Bintree:addRight(child) 24 | if self ~= nil then 25 | self.right = child 26 | return self.right 27 | end 28 | end 29 | 30 | function Bintree:find(data) 31 | if data == self.data then 32 | return self 33 | end 34 | 35 | local output = nil 36 | if type(self.left) == "table" then 37 | output = self.left:find(data) 38 | end 39 | 40 | if type(self.right) == "table" then 41 | output = output or self.right:find(data) or nil 42 | end 43 | 44 | return output 45 | end 46 | 47 | -- remove leaf and replace parent by sibling 48 | function Bintree:removeLeaf(data) 49 | local output = nil 50 | if self.left ~= nil then 51 | if self.left.data == data then 52 | local newSelf = { 53 | data = self.right.data, 54 | left = self.right.left, 55 | right = self.right.right 56 | } 57 | self.data = newSelf.data 58 | self.left = newSelf.left 59 | self.right = newSelf.right 60 | return true 61 | else 62 | output = self.left:removeLeaf(data) or nil 63 | end 64 | end 65 | 66 | if self.right ~= nil then 67 | if self.right.data == data then 68 | local newSelf = { 69 | data = self.left.data, 70 | left = self.left.left, 71 | right = self.left.right 72 | } 73 | self.data = newSelf.data 74 | self.left = newSelf.left 75 | self.right = newSelf.right 76 | return true 77 | else 78 | return output or self.right:removeLeaf(data) or nil 79 | end 80 | end 81 | end 82 | 83 | function Bintree:getSibling(data) 84 | if data == self.data then 85 | return nil 86 | end 87 | 88 | local output = nil 89 | if type(self.left) == "table" then 90 | if self.left.data == data then 91 | return self.right 92 | end 93 | output = self.left:getSibling(data) or nil 94 | end 95 | 96 | if type(self.right) == "table" then 97 | if self.right.data == data then 98 | return self.left 99 | end 100 | output = output or self.right:getSibling(data) or nil 101 | end 102 | 103 | return output or nil 104 | end 105 | 106 | function Bintree:getParent(data) 107 | local output = nil 108 | if type(self.left) == "table" then 109 | if self.left.data == data then 110 | --print('L branch found') 111 | return self 112 | end 113 | output = self.left:getParent(data) or nil 114 | end 115 | 116 | 117 | if type(self.right) == "table" then 118 | if self.right.data == data then 119 | --print('R branch found') 120 | return self 121 | end 122 | output = output or self.right:getParent(data) or nil 123 | end 124 | 125 | return output or nil 126 | 127 | end 128 | 129 | function Bintree:swapLeaves(data1, data2) 130 | local leaf1 = self:find(data1) 131 | local leaf2 = self:find(data2) 132 | 133 | local temp = nil 134 | if leaf1 and leaf2 then 135 | temp = leaf1.data 136 | leaf1.data = leaf2.data 137 | leaf2.data = temp 138 | end 139 | end 140 | 141 | function Bintree.show(node, level) 142 | if level == nil then 143 | level = 0 144 | end 145 | if node ~= nil then 146 | print(string.rep(" ", level) .. "Node[" .. node.data .. "]") 147 | Bintree.show(node.left, level + 1) 148 | Bintree.show(node.right, level + 1) 149 | end 150 | end 151 | 152 | function Bintree.show2(node, level, d) 153 | if level == nil then 154 | level = 0 155 | end 156 | if d == nil then 157 | d='' 158 | end 159 | if node ~= nil then 160 | if type(node.data) == 'number' then 161 | print(string.rep(" ", level) .. d.."Node[" .. node.data .. "]") 162 | else 163 | print(string.rep(" ", level) .. d.."Node[" .. tostring(node.data.x)..' '..tostring(node.data.y)..' '..tostring(node.data.height)..' '..tostring(node.data.width) .. "]") 164 | end 165 | Bintree.show2(node.left, level + 1, 'L_') 166 | Bintree.show2(node.right, level + 1, 'R_') 167 | end 168 | end 169 | 170 | return Bintree 171 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | treetile: Binary Tree-based tiling layout for Awesome 3 3 | 4 | Github:https://github.com/guotsuan/treetile 5 | Folked from Github: https://github.com/RobSis/treesome 6 | License: GNU General Public License v2.0 7 | 8 | 9 | 10 | Because the the split of space is depending on the parent node, which is 11 | current focused client. Therefore it is necessary to set the correct 12 | focus option, "treetile.focusnew". 13 | 14 | If the new created client will automatically gain the focus, for exmaple 15 | in rc.lua with the settings: 16 | 17 | ... 18 | awful.rules.rules = { 19 | { rule = { }, 20 | properties = { focus = awful.client.focus.filter, 21 | ... 22 | 23 | You need to set "treetile.focusnew = true" 24 | 25 | Otherwise, set "treetile.focusnew = false" 26 | --]] 27 | 28 | local awful = require("awful") 29 | local beautiful = require("beautiful") 30 | local Bintree = require("treetile/bintree") 31 | local os = os 32 | local math = math 33 | local ipairs = ipairs 34 | local pairs = pairs 35 | local table = table 36 | local tonumber = tonumber 37 | local tostring = tostring 38 | local type = type 39 | local capi = { 40 | client = client, 41 | tag = tag, 42 | mouse = mouse, 43 | screen = screen, 44 | mousegrabber = mousegrabber 45 | } 46 | local debug = require("gears.debug") 47 | 48 | local treetile = { 49 | focusnew = true, 50 | name = "treetile", 51 | direction = "right" -- the newly created client 52 | -- on the RIGHT or LEFT side of current focus? 53 | } 54 | local naughty = require("naughty") 55 | 56 | 57 | 58 | 59 | -- Globals 60 | local forceSplit = nil 61 | local layoutSwitch = false 62 | local trees = {} 63 | 64 | treetile.name = "treetile" 65 | 66 | -- Layout icon 67 | beautiful.layout_treetile = os.getenv("HOME") .. "/.config/awesome/treetile/layout_icon.png" 68 | 69 | capi.tag.connect_signal("property::layout", function() layoutSwitch = true end) 70 | 71 | local function debuginfo(message) 72 | if type(message) == "table" then 73 | for k,v in pairs(message) do 74 | naughty.notify({ text = "key: "..k.." value: "..tostring(v), timeout = 10 }) 75 | end 76 | elseif type(message) == 'string' then 77 | nid = naughty.notify({ text = message, timeout = 10 }) 78 | else 79 | nid = naughty.notify({ text = tostring(message), timeout = 10 }) 80 | end 81 | end 82 | 83 | -- get an unique identifier of a window 84 | local function hash(client) 85 | if client then 86 | return client.window 87 | else 88 | return nil 89 | end 90 | end 91 | 92 | function Bintree:update_nodes_geo(parent_geo, geo_table) 93 | 94 | local left_node_geo = nil 95 | local right_node_geo = nil 96 | if type(self.data) == 'number' then 97 | -- This sibling node is a client. 98 | -- Just need to resize this client to the size of its geometry of 99 | -- parent node (the empty workear left by the killed client 100 | -- together with orignal area occupied by this sibling client). 101 | 102 | if type(parent_geo) == "table" then 103 | geo_table[self.data] = awful.util.table.clone(parent_geo) 104 | else 105 | debug.print_error ('geometry table error errors') 106 | end 107 | 108 | return 109 | end 110 | 111 | if type(self.data) == 'table' then 112 | -- the sibling is another table, need to update the geometry of all descendants. 113 | local now_geo = nil 114 | now_geo = awful.util.table.clone(self.data) 115 | self.data = awful.util.table.clone(parent_geo) 116 | 117 | if type(self.left.data) == 'number' then 118 | left_node_geo = awful.util.table.clone(geo_table[self.left.data]) 119 | end 120 | 121 | 122 | if type(self.right.data) == 'number' then 123 | right_node_geo = awful.util.table.clone(geo_table[self.right.data]) 124 | end 125 | 126 | if type(self.left.data) == 'table' then 127 | left_node_geo = awful.util.table.clone(self.left.data) 128 | end 129 | 130 | if type(self.right.data) == 'table' then 131 | right_node_geo = awful.util.table.clone(self.right.data) 132 | end 133 | 134 | 135 | -- {{{ vertical split 136 | if math.abs(left_node_geo.x - right_node_geo.x) < 0.2 then 137 | -- Nodes are split in vertical way 138 | if math.abs(parent_geo.width - now_geo.width ) > 0.2 then 139 | left_node_geo.width = parent_geo.width 140 | right_node_geo.width = parent_geo.width 141 | 142 | local new_x = parent_geo.x 143 | 144 | left_node_geo.x = new_x 145 | right_node_geo.x = new_x 146 | end 147 | 148 | if math.abs(parent_geo.height - now_geo.height ) > 0.2 then 149 | 150 | if treetile.direction == 'left' then 151 | left_node_geo, right_node_geo = right_node_geo, left_node_geo 152 | end 153 | 154 | local new_y = parent_geo.y 155 | 156 | r_l_ratio = left_node_geo.height / now_geo.height 157 | 158 | left_node_geo.height = parent_geo.height * r_l_ratio 159 | right_node_geo.height = parent_geo.height - left_node_geo.height 160 | 161 | left_node_geo.y = new_y 162 | right_node_geo.y = new_y + left_node_geo.height 163 | 164 | end 165 | end 166 | -- }}} 167 | 168 | -- {{{ horizontal split 169 | if math.abs(left_node_geo.y - right_node_geo.y) < 0.2 then 170 | -- Nodes are split in horizontal way 171 | if math.abs(parent_geo.height - now_geo.height) > 0.2 then 172 | left_node_geo.height = parent_geo.height 173 | right_node_geo.height = parent_geo.height 174 | 175 | local new_y = parent_geo.y 176 | 177 | left_node_geo.y = new_y 178 | right_node_geo.y = new_y 179 | end 180 | 181 | if math.abs(parent_geo.width - now_geo.width) > 0.2 then 182 | 183 | if treetile.direction == 'left' then 184 | left_node_geo, right_node_geo = right_node_geo, left_node_geo 185 | end 186 | 187 | local new_x = parent_geo.x 188 | 189 | r_l_ratio = left_node_geo.width / now_geo.width 190 | 191 | left_node_geo.width = parent_geo.width * r_l_ratio 192 | right_node_geo.width = parent_geo.width - left_node_geo.width 193 | 194 | left_node_geo.x = new_x 195 | right_node_geo.x = new_x + left_node_geo.width 196 | 197 | end 198 | 199 | end 200 | -- }}} 201 | 202 | 203 | if type(self.left.data) == 'number' then 204 | geo_table[self.left.data].x = left_node_geo.x 205 | geo_table[self.left.data].y = left_node_geo.y 206 | geo_table[self.left.data].height = left_node_geo.height 207 | geo_table[self.left.data].width = left_node_geo.width 208 | end 209 | 210 | if type(self.right.data) == 'number' then 211 | geo_table[self.right.data].x = right_node_geo.x 212 | geo_table[self.right.data].y = right_node_geo.y 213 | geo_table[self.right.data].height = right_node_geo.height 214 | geo_table[self.right.data].width = right_node_geo.width 215 | end 216 | 217 | 218 | if type(self.left.data) == 'table' then 219 | self.left:update_nodes_geo(left_node_geo, geo_table) 220 | end 221 | 222 | if type(self.right.data) == 'table' then 223 | self.right:update_nodes_geo(right_node_geo, geo_table) 224 | end 225 | 226 | 227 | end 228 | 229 | end 230 | 231 | 232 | local function table_find(tbl, item) 233 | for key, value in pairs(tbl) do 234 | if value == item then return key end 235 | end 236 | return false 237 | end 238 | 239 | local function table_diff(table1, table2) 240 | local diffList = {} 241 | for i,v in ipairs(table1) do 242 | if table2[i] ~= v then 243 | table.insert(diffList, v) 244 | end 245 | end 246 | if #diffList == 0 then 247 | diffList = nil 248 | end 249 | return diffList 250 | end 251 | 252 | -- get ancestors of node with given data 253 | function Bintree:trace(data, path, dir) 254 | if path then 255 | table.insert(path, {split=self.data, direction=dir}) 256 | end 257 | 258 | if data == self.data then 259 | return path 260 | end 261 | 262 | if type(self.left) == "table" then 263 | if (self.left:trace(data, path, "left")) then 264 | return true 265 | end 266 | end 267 | 268 | if type(self.right) == "table" then 269 | if (self.right:trace(data, path, "right")) then 270 | return true 271 | end 272 | end 273 | 274 | if path then 275 | table.remove(path) 276 | end 277 | end 278 | 279 | -- remove all leaves with data that don't appear in given table 280 | -- and only remove clients 281 | function Bintree:filterClients(node, clients) 282 | if node then 283 | if node.data and not table_find(clients, node.data) and 284 | type(node.data) == 'number' then 285 | self:removeLeaf(node.data) 286 | end 287 | 288 | local output = nil 289 | if node.left then 290 | self:filterClients(node.left, clients) 291 | end 292 | 293 | if node.right then 294 | self:filterClients(node.right, clients) 295 | end 296 | end 297 | end 298 | 299 | function treetile.horizontal() 300 | forceSplit = "horizontal" 301 | debuginfo('Next split is left right (|) split') 302 | end 303 | 304 | function treetile.vertical() 305 | forceSplit = "vertical" 306 | debuginfo('Next split is upper bottom (-)split') 307 | end 308 | 309 | local function do_treetile(p) 310 | local old_clients = nil 311 | local area = p.workarea 312 | local n = #p.clients 313 | local gs = p.geometries 314 | 315 | local tag = tostring(p.tag or capi.screen[p.screen].selected_tag 316 | or awful.tag.selected(capi.mouse.screen)) 317 | 318 | 319 | if not trees[tag] then 320 | trees[tag] = { 321 | t = nil, 322 | lastFocus = nil, 323 | clients = nil, 324 | geo_t = nil, 325 | geo = nil, 326 | n = 0 327 | } 328 | end 329 | 330 | -- t is tree structure to record all the clients and the way of splitting 331 | -- geo_t is the tree structure to record the geometry of all nodes/clients 332 | -- of the parent nodes (the over-all geometry of all siblings together) 333 | 334 | if trees[tag] ~= nil then 335 | -- should find a better to handle this 336 | if treetile.focusnew then 337 | focus = awful.client.focus.history.get(p.screen,1) 338 | else 339 | focus = capi.client.focus 340 | end 341 | 342 | if focus ~= nil then 343 | local isfloat 344 | if type(focus.floating) == 'boolean' then 345 | isfloat = focus.floating 346 | else 347 | isfloat = awful.client.floating.get(focus) 348 | end 349 | 350 | if isfloat then 351 | focus = nil 352 | else 353 | trees[tag].lastFocus = focus 354 | end 355 | end 356 | 357 | end 358 | 359 | -- rearange only on change 360 | local changed = 0 361 | local update = false 362 | 363 | if trees[tag].n ~= n then 364 | if not trees[tag].n or n > trees[tag].n then 365 | changed = 1 366 | else 367 | changed = -1 368 | end 369 | trees[tag].n = n 370 | else 371 | if trees[tag].clients then 372 | local diff = table_diff(p.clients, trees[tag].clients) 373 | if diff and #diff == 2 then 374 | trees[tag].t:swapLeaves(hash(diff[1]), hash(diff[2])) 375 | trees[tag].geo_t:swapLeaves(hash(diff[1]), hash(diff[2])) 376 | trees[tag].geo[hash(diff[1])], trees[tag].geo[hash(diff[2])] 377 | = trees[tag].geo[hash(diff[2])], trees[tag].geo[hash(diff[1])] 378 | update=true 379 | end 380 | end 381 | end 382 | 383 | trees[tag].clients = p.clients 384 | 385 | -- some client removed. update the trees 386 | if changed < 0 then 387 | if n > 0 then 388 | local tokens = {} 389 | for i, c in ipairs(p.clients) do 390 | tokens[i] = hash(c) 391 | end 392 | 393 | for clid, _ in pairs(trees[tag].geo) do 394 | if awful.util.table.hasitem(tokens, clid) == nil then 395 | -- update the size of clients left, fill the empty space left by the killed client 396 | 397 | local sib_node = trees[tag].geo_t:getSibling(clid) 398 | local parent = trees[tag].geo_t:getParent(clid) 399 | local parent_geo = nil 400 | 401 | if parent then 402 | parent_geo = parent.data 403 | end 404 | 405 | if sib_node ~= nil then 406 | sib_node:update_nodes_geo(parent_geo, trees[tag].geo) 407 | end 408 | 409 | local pos = awful.util.table.hasitem(trees[tag].geo, clid) 410 | table.remove(trees[tag].geo, pos) 411 | end 412 | end 413 | 414 | trees[tag].geo_t:filterClients(trees[tag].geo_t, tokens) 415 | trees[tag].t:filterClients(trees[tag].t, tokens) 416 | 417 | --awful.client.jumpto(trees[tag].lastFocus) 418 | else 419 | trees[tag] = nil 420 | end 421 | end 422 | 423 | 424 | -- one or more clients are added. Put them in the tree. 425 | local prevClient = nil 426 | local nextSplit = 0 427 | 428 | if changed > 0 then 429 | for i, c in ipairs(p.clients) do 430 | if not trees[tag].t or not trees[tag].t:find(hash(c)) then 431 | if focus == nil then 432 | focus = trees[tag].lastFocus 433 | end 434 | 435 | local focusNode = nil 436 | local focusGeometry = nil 437 | local focusNode_geo_t = nil 438 | local focusId = nil 439 | 440 | if trees[tag].t and focus and hash(c) ~= hash(focus) and not layoutSwitch then 441 | -- Find the parent node for splitting 442 | focusNode = trees[tag].t:find(hash(focus)) 443 | focusNode_geo_t = trees[tag].geo_t:find(hash(focus)) 444 | focusGeometry = focus:geometry() 445 | focusId = hash(focus) 446 | else 447 | -- the layout was switched with more clients to order at once 448 | if prevClient then 449 | focusNode = trees[tag].t:find(hash(prevClient)) 450 | focusNode_geo_t = trees[tag].geo_t:find(hash(prevClient)) 451 | nextSplit = (nextSplit + 1) % 2 452 | focusGeometry = trees[tag].geo[hash(prevClient)] 453 | focusId = hash(prevClient) 454 | 455 | else 456 | if not trees[tag].t then 457 | -- create a new root 458 | trees[tag].t = Bintree.new(hash(c)) 459 | focusGeometry = { 460 | width = 0, 461 | height = 0 462 | } 463 | trees[tag].geo_t = Bintree.new(hash(c)) 464 | trees[tag].geo = {} 465 | trees[tag].geo[hash(c)] = awful.util.table.clone(area) 466 | focusId = hash(c) 467 | --focusNode = trees[tag].t:find(hash(c)) 468 | --focusNode_geo_t = trees[tag].geo_t:find(hash(c)) 469 | end 470 | end 471 | end 472 | 473 | -- {{{ if focusNode exists 474 | if focusNode then 475 | if focusGeometry == nil then 476 | local splits = {"horizontal", "vertical"} 477 | focusNode.data = splits[nextSplit + 1] 478 | else 479 | if (forceSplit ~= nil) then 480 | 481 | focusNode.data = forceSplit 482 | else 483 | if (focusGeometry.width <= focusGeometry.height) then 484 | focusNode.data = "vertical" 485 | else 486 | focusNode.data = "horizontal" 487 | end 488 | end 489 | end 490 | 491 | if treetile.direction == 'right' then 492 | focusNode:addLeft(Bintree.new(focusId)) 493 | focusNode_geo_t:addLeft(Bintree.new(focusId)) 494 | focusNode:addRight(Bintree.new(hash(c))) 495 | focusNode_geo_t:addRight(Bintree.new(hash(c))) 496 | else 497 | focusNode:addRight(Bintree.new(focusId)) 498 | focusNode_geo_t:addRight(Bintree.new(focusId)) 499 | focusNode:addLeft(Bintree.new(hash(c))) 500 | focusNode_geo_t:addLeft(Bintree.new(hash(c))) 501 | end 502 | 503 | local useless_gap = tonumber(beautiful.useless_gap) 504 | if useless_gap == nil then 505 | useless_gap = 0 506 | else 507 | useless_gap = useless_gap * 2.0 508 | end 509 | 510 | local avail_geo =nil 511 | 512 | if focusGeometry then 513 | if focusGeometry.height == 0 and focusGeometry.width == 0 then 514 | avail_geo = area 515 | else 516 | avail_geo = focusGeometry 517 | end 518 | else 519 | avail_geo = area 520 | end 521 | 522 | local new_c = {} 523 | local old_focus_c = {} 524 | 525 | -- put the geometry of parament node into table too 526 | focusNode_geo_t.data = awful.util.table.clone(avail_geo) 527 | 528 | if focusNode.data == "horizontal" then 529 | new_c.width = math.floor((avail_geo.width - useless_gap) / 2.0 ) 530 | new_c.height = avail_geo.height 531 | old_focus_c.width = math.floor((avail_geo.width - useless_gap) / 2.0 ) 532 | old_focus_c.height = avail_geo.height 533 | old_focus_c.y = avail_geo.y 534 | new_c.y = avail_geo.y 535 | 536 | if treetile.direction == "right" then 537 | new_c.x = avail_geo.x + new_c.width + useless_gap 538 | old_focus_c.x = avail_geo.x 539 | else 540 | new_c.x = avail_geo.x 541 | old_focus_c.x = avail_geo.x + new_c.width - useless_gap 542 | end 543 | 544 | 545 | elseif focusNode.data == "vertical" then 546 | new_c.height = math.floor((avail_geo.height - useless_gap) / 2.0 ) 547 | new_c.width = avail_geo.width 548 | old_focus_c.height = math.floor((avail_geo.height - useless_gap) / 2.0 ) 549 | old_focus_c.width = avail_geo.width 550 | old_focus_c.x = avail_geo.x 551 | new_c.x = avail_geo.x 552 | 553 | if treetile.direction == "right" then 554 | new_c.y = avail_geo.y + new_c.height + useless_gap 555 | old_focus_c.y = avail_geo.y 556 | else 557 | new_c.y = avail_geo.y 558 | old_focus_c.y =avail_geo.y + new_c.height - useless_gap 559 | end 560 | 561 | end 562 | 563 | -- put geometry of clients into tables 564 | if focusId then 565 | trees[tag].geo[focusId] = old_focus_c 566 | trees[tag].geo[hash(c)] = new_c 567 | end 568 | 569 | 570 | end 571 | end 572 | -- }}} 573 | 574 | prevClient = c 575 | end 576 | forceSplit = nil 577 | end 578 | 579 | 580 | -- update the geometries of all clients 581 | if changed ~= 0 or layoutSwitch or update then 582 | 583 | if n >= 1 then 584 | for i, c in ipairs(p.clients) do 585 | local hints = {} 586 | 587 | local geo = nil 588 | geo = trees[tag].geo[hash(c)] 589 | if type(geo) == 'table' then 590 | --gs[c] = geo 591 | c:geometry(geo) 592 | --hints.width, hints.height = c:apply_size_hints(geo.width, geo.height) 593 | else 594 | debug.print_error("wrong geometry in treetile/init.lua") 595 | end 596 | end 597 | end 598 | 599 | layoutSwitch=false 600 | end 601 | end 602 | 603 | local function clip(v, min, max) 604 | return math.max(math.min(v,max), min) 605 | end 606 | 607 | 608 | function treetile.resize_client(inc) --{{{ resize client 609 | -- inc: percentage of change: 0.01, 0.99 with +/- 610 | local focus_c = capi.client.focus 611 | local g = focus_c:geometry() 612 | 613 | local tag = tostring(focus_c.screen.selected_tag or awful.tag.selected(focus_c.screen)) 614 | 615 | local parent_node = trees[tag].geo_t:getParent(hash(focus_c)) 616 | local parent_c = trees[tag].t:getParent(hash(focus_c)) 617 | local sib_node = trees[tag].geo_t:getSibling(hash(focus_c)) 618 | local sib_node_geo 619 | if type(sib_node.data) == "number" then 620 | sib_node_geo = trees[tag].geo[sib_node.data] 621 | else 622 | sib_node_geo = sib_node.data 623 | end 624 | 625 | local parent_geo 626 | 627 | if parent_node then 628 | parent_geo = parent_node.data 629 | else 630 | return 631 | end 632 | 633 | local new_geo = {} 634 | local new_sib = {} 635 | 636 | local min_y = 20.0 637 | local min_x = 20.0 638 | 639 | local useless_gap = tonumber(beautiful.useless_gap) 640 | if useless_gap == nil then 641 | useless_gap = 0 642 | else 643 | useless_gap = useless_gap * 2.0 644 | end 645 | 646 | new_geo.x = g.x 647 | new_geo.y = g.y 648 | new_geo.width = g.width 649 | new_geo.height = g.height 650 | 651 | local fact_y 652 | local fact_x 653 | 654 | if parent_c.data =='vertical' then 655 | fact_y = math.ceil(clip(g.height * clip(math.abs(inc), 0.01, 0.99), 5, 30)) 656 | if inc < 0 then 657 | fact_y = -fact_y 658 | end 659 | 660 | end 661 | 662 | if parent_c.data =='horizontal' then 663 | fact_x = math.ceil(clip(g.width * clip(math.abs(inc), 0.01, 0.99), 5, 30)) 664 | if inc < 0 then 665 | fact_x = - fact_x 666 | end 667 | end 668 | 669 | 670 | if parent_c.data =='vertical' then 671 | -- determine which is on the right side 672 | if g.y > sib_node_geo.y then 673 | new_geo.height = clip(g.height - fact_y, min_y, parent_geo.height - min_y) 674 | new_geo.y = parent_geo.y + parent_geo.height - new_geo.height 675 | 676 | new_sib.x = parent_geo.x 677 | new_sib.y = parent_geo.y 678 | new_sib.width = parent_geo.width 679 | new_sib.height = parent_geo.height - new_geo.height - useless_gap 680 | else 681 | new_geo.y = g.y 682 | new_geo.height = clip(g.height + fact_y, min_y, parent_geo.height - min_y) 683 | 684 | new_sib.x = new_geo.x 685 | new_sib.y = new_geo.y + new_geo.height + useless_gap 686 | new_sib.width = parent_geo.width 687 | new_sib.height = parent_geo.height - new_geo.height - useless_gap 688 | end 689 | end 690 | 691 | 692 | if parent_c.data =='horizontal' then 693 | -- determine which is on the top side 694 | if g.x > sib_node_geo.x then 695 | 696 | new_geo.width = clip(g.width - fact_x, min_x, parent_geo.width - min_x) 697 | new_geo.x = parent_geo.x + parent_geo.width - new_geo.width 698 | 699 | new_sib.y = parent_geo.y 700 | new_sib.x = parent_geo.x 701 | new_sib.height = parent_geo.height 702 | new_sib.width = parent_geo.width - new_geo.width - useless_gap 703 | else 704 | new_geo.x = g.x 705 | new_geo.width = clip(g.width + fact_x, min_x, parent_geo.width - min_x) 706 | 707 | new_sib.y = parent_geo.y 708 | new_sib.x = parent_geo.x + new_geo.width + useless_gap 709 | new_sib.height = parent_geo.height 710 | new_sib.width = parent_geo.width - new_geo.width - useless_gap 711 | end 712 | end 713 | 714 | 715 | trees[tag].geo[hash(focus_c)] = new_geo 716 | 717 | local sib_node = trees[tag].geo_t:getSibling(hash(focus_c)) 718 | 719 | if sib_node ~= nil then 720 | sib_node:update_nodes_geo(new_sib, trees[tag].geo) 721 | end 722 | 723 | 724 | for _, c in ipairs(trees[tag].clients) do 725 | local geo = nil 726 | geo = trees[tag].geo[hash(c)] 727 | if type(geo) == 'table' then 728 | c:geometry(geo) 729 | else 730 | debug.print_error("wrong geometry in init.lua") 731 | end 732 | end 733 | 734 | end--}}} 735 | 736 | 737 | function treetile.arrange(p) 738 | return do_treetile(p) 739 | end 740 | 741 | -- no implimented yet, do not use it! 742 | -- resizing should only happen between the siblings? I guess so 743 | -- 744 | local function mouse_resize_handler(c, _, _, _)--{{{ 745 | local orientation = orientation or "tile" 746 | local wa = capi.screen[c.screen].workarea 747 | local tag = tostring(c.screen.selected_tag or awful.tag.selected(c.screen)) 748 | local cursor 749 | local g = c:geometry() 750 | local offset = 0 751 | local corner_coords 752 | 753 | local parent_c = trees[tag].t:getParent(hash(c)) 754 | 755 | local parent_node = trees[tag].geo_t:getParent(hash(c)) 756 | local parent_geo 757 | 758 | local new_y = nil 759 | local new_x = nil 760 | 761 | local sib_node = trees[tag].geo_t:getSibling(hash(c)) 762 | local sib_node_geo 763 | if type(sib_node.data) == "number" then 764 | sib_node_geo = trees[tag].geo[sib_node.data] 765 | else 766 | sib_node_geo = sib_node.data 767 | end 768 | 769 | if parent_node then 770 | parent_geo = parent_node.data 771 | else 772 | return 773 | end 774 | if parent_c then 775 | if parent_c.data =='vertical' then 776 | cursor = "sb_v_double_arrow" 777 | new_y = math.max(g.y, sib_node_geo.y) 778 | new_x = g.x + g.width / 2 779 | end 780 | 781 | if parent_c.data =='horizontal' then 782 | cursor = "sb_h_double_arrow" 783 | new_x = math.max(g.x, sib_node_geo.x) 784 | new_y = g.y + g.height / 2 785 | end 786 | end 787 | 788 | 789 | corner_coords = { x = new_x, y = new_y } 790 | 791 | capi.mouse.coords(corner_coords) 792 | 793 | local prev_coords = {} 794 | capi.mousegrabber.run(function (_mouse) 795 | for k, v in ipairs(_mouse.buttons) do 796 | if v then 797 | prev_coords = { x =_mouse.x, y = _mouse.y } 798 | local fact_x = (_mouse.x - corner_coords.x) 799 | local fact_y = (_mouse.y - corner_coords.y) 800 | 801 | local new_geo = {} 802 | local new_sib = {} 803 | 804 | local min_x = 15.0 805 | local min_y = 15.0 806 | 807 | new_geo.x = g.x 808 | new_geo.y = g.y 809 | new_geo.width = g.width 810 | new_geo.height = g.height 811 | 812 | if parent_c.data =='vertical' then 813 | if g.y > sib_node_geo.y then 814 | new_geo.height = clip(g.height - fact_y, min_y, parent_geo.height - min_y) 815 | new_geo.y= clip(_mouse.y, sib_node_geo.y + min_y, parent_geo.y + parent_geo.height - min_y) 816 | 817 | new_sib.x = parent_geo.x 818 | new_sib.y = parent_geo.y 819 | new_sib.width = parent_geo.width 820 | new_sib.height = parent_geo.height - new_geo.height 821 | else 822 | new_geo.y = g.y 823 | new_geo.height = clip(g.height + fact_y, min_y, parent_geo.height - min_y) 824 | 825 | new_sib.x = new_geo.x 826 | new_sib.y = new_geo.y + new_geo.height 827 | new_sib.width = parent_geo.width 828 | new_sib.height = parent_geo.height - new_geo.height 829 | end 830 | end 831 | 832 | 833 | if parent_c.data =='horizontal' then 834 | if g.x > sib_node_geo.x then 835 | new_geo.width = clip(g.width - fact_x, min_x, parent_geo.width - min_x) 836 | new_geo.x = clip(_mouse.x, sib_node_geo.x + min_x, parent_geo.x + parent_geo.width - min_x) 837 | 838 | new_sib.y = parent_geo.y 839 | new_sib.x = parent_geo.x 840 | new_sib.height = parent_geo.height 841 | new_sib.width = parent_geo.width - new_geo.width 842 | else 843 | new_geo.x = g.x 844 | new_geo.width = clip(g.width + fact_x, min_x, parent_geo.width - min_x) 845 | 846 | new_sib.y = parent_geo.y 847 | new_sib.x = parent_geo.x + new_geo.width 848 | new_sib.height = parent_geo.height 849 | new_sib.width = parent_geo.width - new_geo.width 850 | end 851 | end 852 | 853 | 854 | trees[tag].geo[hash(c)] = new_geo 855 | 856 | local sib_node = trees[tag].geo_t:getSibling(hash(c)) 857 | 858 | if sib_node ~= nil then 859 | sib_node:update_nodes_geo(new_sib, trees[tag].geo) 860 | end 861 | 862 | 863 | for _, c in ipairs(trees[tag].clients) do 864 | local geo = nil 865 | geo = trees[tag].geo[hash(c)] 866 | if type(geo) == 'table' then 867 | c:geometry(geo) 868 | else 869 | debug.print_error ("wrong geometry in init.lua") 870 | end 871 | end 872 | return true 873 | end 874 | end 875 | return prev_coords.x == _mouse.x and prev_coords.y == _mouse.y 876 | end, cursor) 877 | end 878 | --}}} 879 | 880 | function treetile.mouse_resize_handler(c, corner, x, y) 881 | mouse_resize_handler(c, corner,x,y) 882 | end 883 | 884 | 885 | return treetile 886 | --------------------------------------------------------------------------------