diff --git a/.gitignore b/.gitignore index c0a1b36..d3741c6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ helpers widget module geometries +outlines \ No newline at end of file diff --git a/backham.lua b/backham.lua new file mode 100644 index 0000000..93c0a08 --- /dev/null +++ b/backham.lua @@ -0,0 +1,27 @@ + +--+ allows automatically focusing back to the previous client +--> on window close (unmanage) or minimize. + +local get_client_info = require("machina.methods").get_client_info + +-------------------------------------------------------------------> methods ; + +function backham(c) + local s = awful.screen.focused() + local back_to = awful.client.focus.history.get(s, 0) + local active_region = get_client_info(c).active_region + + if not (active_region and client.floating) and back_to then + client.focus = back_to + back_to:raise() + end +end + +--------------------------------------------------------------------> signal ; + +client.connect_signal("property::minimized", backham) +--+ attach to minimized state + +client.connect_signal("unmanage", backham) +--+ attach to closed state + diff --git a/geoms.lua b/geoms.lua new file mode 100644 index 0000000..3408f05 --- /dev/null +++ b/geoms.lua @@ -0,0 +1,71 @@ +local geoms = {} + +geoms.crt43 = function () + return { + x=awful.screen.focused().workarea.width - client.focus:geometry().width, + y=awful.screen.focused().workarea.height - client.focus:geometry().height + awful.screen.focused().workarea.y, + width=1280, + height=1024 + } +end --|awful.screen.focused().workarea.y is required for + --|multiple monitors to relocate properly. + +geoms.p1080 = function () + return { + x=awful.screen.focused().workarea.width - client.focus:geometry().width, + y=awful.screen.focused().workarea.height - client.focus:geometry().height + awful.screen.focused().workarea.y, + width=awful.screen.focused().workarea.width * 0.65, + height=awful.screen.focused().workarea.height * 0.90 + } +end + +geoms.p720 = function () + return { + x=awful.screen.focused().workarea.width - client.focus:geometry().width, + y=awful.screen.focused().workarea.height - client.focus:geometry().height + awful.screen.focused().workarea.y, + width=awful.screen.focused().workarea.width * 0.40, + height=awful.screen.focused().workarea.height * 0.45 + } +end + +geoms["center"] = function(useless_gap) + return { + x=awful.screen.focused().workarea.width/2 - client.focus.width/2, + y=awful.screen.focused().workarea.height/2 - client.focus.height/2 + awful.screen.focused().workarea.y + } +end + +geoms["top-left"] = function(useless_gap) + return { + x=useless_gap, + y=useless_gap + awful.screen.focused().workarea.y + } +end + +geoms["bottom-left"] = function(useless_gap) + return { + x=useless_gap, + y=awful.screen.focused().workarea.height - useless_gap - client.focus.height + awful.screen.focused().workarea.y + } +end + +geoms["top-right"] = function(useless_gap) + return { + x=awful.screen.focused().workarea.width - useless_gap - client.focus.width, + y=useless_gap + awful.screen.focused().workarea.y + } +end + +geoms["bottom-right"] = function(useless_gap) + return { + x=awful.screen.focused().workarea.width - useless_gap - client.focus.width, + y=awful.screen.focused().workarea.height - useless_gap - client.focus.height + awful.screen.focused().workarea.y + } +end + +geoms.clients = { + Subl=geoms.p1080, + Byobu=geoms.p720, +} + +return geoms \ No newline at end of file diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..860f739 --- /dev/null +++ b/helpers.lua @@ -0,0 +1,139 @@ + +local function old_shuffle(direction) + return function() + local tablist = region_tablist() + + if direction == "backward" then + local prev_client = nil + for i = #tablist, 1, -1 do + prev_client = tablist[i] + prev_client:emit_signal("request::activate", "mouse_enter",{raise = true}) + break --|activate previous client + end + return + end + + if direction == "forward" then + local next_client = nil + for _, cc in ipairs(tablist) do + client.focus:lower() + next_client = tablist[_+1] + next_client:emit_signal("request::activate", "mouse_enter",{raise = true}) + break --|activate next client + end + return + end + end +end --| + --|this uses the old hack that doesn't rely on + --|global_client_index. keeping it here for historical + --|reasons in case we need this again. + + +local function reload(m) + if package.loaded[m] then + local attrs = {} + for key,value in pairs(package.loaded[m]) do + attrs[key] = value + end + package.loaded[m] = nil + temp_module = require(tostring(m)) + for key,value in pairs(attrs) do + temp_module[key] = value + end + else + temp_module = require(m) + end + return temp_module +end + + +local function getlowest(table) + local low = math.huge + local index + for i, v in pairs(table) do + if v < low then + low = v + index = i + end + end + return index +end + + +local function compare(a,b) + return a.v < b.v +end + +local function tablelength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end --|return table length + +local function set_contains(set, key) + return set[key] ~= nil +end --|tests table if key exists + +local function get_client_ix(id, ids) + for i, c in ipairs(ids) do + if id == c.window then + return i + end + end +end --|finds out where id is located inside ids. + +local function clear_tabbar(c, position) + if not c then return end + + position = position or "bottom" + local titlebar = awful.titlebar(c, {size=3, position=position}) + awful.titlebar(c, {size=0, position="left"}) + awful.titlebar(c, {size=0, position="right"}) + titlebar:setup{ + layout=wibox.layout.flex.horizontal, nil + } +end --|clears bottom tabbar + +-------------------------------------------------------------- exports -- ; + +return { + set_contains = set_contains, + tablelength = tablelength, + compare = compare, + getlowest = getlowest, + get_client_ix = get_client_ix, + reload = reload, + clear_tabbar = clear_tabbar +} + + + + + + + + + -- d:connect_signal("focus", function(_) + -- local poo = awful.titlebar(d, {position="bottom", size="14"}) + -- -- local dee = poo.get_all_children({"335544341"}) + -- local dee = poo:get_children_by_id("335544341") + + -- file = io.open("/tmp/lua.txt", "w") + -- io.output(file) + -- io.write(inspect(poo)) + -- io.close(file) + + -- naughty.notify({text=inspect(dee)}) + -- end) + + + -- -- gears.timer.delayed_call(function (d) + -- -- -- local active_region = get_client_region(d) + -- -- naughty.notify({text="here"}) + + -- -- if active_region then + -- -- c.region = active_region + -- -- draw_tabbar(active_region) + -- -- end + -- -- end, c) diff --git a/init.lua b/init.lua index c17e583..f292b4f 100644 --- a/init.lua +++ b/init.lua @@ -1,21 +1,15 @@ ---------------------------------------------------------- dependencies -- ; --- local inspect = require('inspect') local capi = {root=root} -local gears = require("gears") -local naughty = require("naughty") local awful = require("awful") local modkey = "Mod4" -local machina = require('awesomewm-machina.methods') -local compare = machina.compare -local region_tablist = machina.region_tablist +local machina = require("machina.methods") +local backham = require("machina.backham") local focus_by_direction = machina.focus_by_direction -local get_active_regions = machina.get_active_regions local shift_by_direction = machina.shift_by_direction local expand_horizontal = machina.expand_horizontal -local geoms = machina.geoms local shuffle = machina.shuffle local my_shifter = machina.my_shifter local expand_vertical = machina.expand_vertical @@ -25,62 +19,90 @@ local toggle_always_on = machina.toggle_always_on ---------------------------------------------------------- key bindings -- ; local bindings = { + awful.key({modkey}, "[", my_shifter("backward")), + awful.key({modkey}, "]", my_shifter("forward")), + --▨ move + + awful.key({modkey, "Shift"}, "[", my_shifter("backward", "swap")), + awful.key({modkey, "Shift"}, "]", my_shifter("forward", "swap")), + --▨ swap + awful.key({modkey}, ";", shuffle("backward")), awful.key({modkey}, "'", shuffle("forward")), - --▨ shuffle decks + --▨ shuffle + + awful.key({modkey}, "j", focus_by_direction("left")), + awful.key({modkey}, "k", focus_by_direction("down")), + awful.key({modkey}, "l", focus_by_direction("right")), + awful.key({modkey}, "i", focus_by_direction("up")), + --▨ focus awful.key({modkey, "Shift"}, "j", shift_by_direction("left")), awful.key({modkey, "Shift"}, "l", shift_by_direction("right")), awful.key({modkey, "Shift"}, "k", shift_by_direction("down")), awful.key({modkey, "Shift"}, "i", shift_by_direction("up")), - --▨ move (directional) + --▨ move - awful.key({modkey}, "[", my_shifter("backward")), - awful.key({modkey}, "]", my_shifter("forward")), - --▨ move (clockwise) + awful.key({modkey, "Control"}, "j", shift_by_direction("left", "swap")), + awful.key({modkey, "Control"}, "l", shift_by_direction("right", "swap")), + awful.key({modkey, "Control"}, "k", shift_by_direction("down", "swap")), + awful.key({modkey, "Control"}, "i", shift_by_direction("up","swap")), + --▨ swap - awful.key({modkey, "Shift"}, "Insert", move_to("top-left")), - awful.key({modkey, "Shift"}, "Page_Up", move_to("top-right")), - awful.key({modkey, "Shift"}, "Home", move_to("center")), - awful.key({modkey, "Shift"}, "End", toggle_always_on), - awful.key({modkey, "Shift"}, "Delete", move_to("bottom-left")), - awful.key({modkey, "Shift"}, "Page_Down", move_to("bottom-right")), - --▨ move (positional) - - awful.key({modkey, "Shift" }, "[", shift_by_direction("left", true)), - awful.key({modkey, "Shift" }, "]", shift_by_direction("right", true)), - awful.key({modkey, "Control"}, "[", shift_by_direction("down", true)), - awful.key({modkey, "Control"}, "]", shift_by_direction("up", true)), - --▨ swap (directional) - - awful.key({modkey}, "Insert", expand_horizontal("left")), - awful.key({modkey}, "Page_Up", expand_horizontal("right")), - awful.key({modkey}, "Home", expand_horizontal("center")), - awful.key({modkey}, "Page_Down", expand_vertical), + awful.key({modkey, "Shift"}, "Insert", expand_horizontal("left")), + awful.key({modkey, "Shift"}, "Page_Up", expand_horizontal("right")), + awful.key({modkey, "Shift"}, "Page_Down", expand_vertical), --▨ expand (neighbor) + awful.key({modkey}, "Insert", move_to("top-left")), + awful.key({modkey}, "Page_Up", move_to("top-right")), + awful.key({modkey}, "Delete", move_to("bottom-left")), + awful.key({modkey}, "Page_Down", move_to("bottom-right")), + --▨ move (positional) + + awful.key({modkey}, "Home", expand_horizontal("center")), awful.key({modkey}, "End", function() client.focus.maximized_vertical = false client.focus.maximized_horizontal = false awful.client.floating.toggle() end), --|toggle floating status + awful.key({modkey, "Shift"}, "End", toggle_always_on), + awful.key({modkey, "Shift"}, "Home", move_to("center")), + + awful.key({modkey,}, "o", function () + c = client.focus + if not c then return true end + + if not c.floating then + c:geometry({width=300, height=300}) + end --|to avoid machi's auto expansion + + c:move_to_screen() + gears.timer.delayed_call(function () + c:emit_signal("request::activate", "mouse_enter",{raise = true}) + end) + end), --|client teleport to other screen + awful.key({modkey}, "Left", focus_by_direction("left")), - awful.key({modkey}, "j", focus_by_direction("left")), - awful.key({modkey}, "Down", focus_by_direction("down")), - awful.key({modkey}, "k", focus_by_direction("down")), - awful.key({modkey}, "Right", focus_by_direction("right")), - awful.key({modkey}, "l", focus_by_direction("right")), - awful.key({modkey}, "Up", focus_by_direction("up")), - awful.key({modkey}, "i", focus_by_direction("up")) --▨ focus } --------------------------------------------------------------- signals -- ; +client.connect_signal("manage", function(c) + c.maximized = false + c.maximized_horizontal = false + c.maximized_vertical = false +end) --|during reload maximized clients get messed up, as machi + --|also tries to best fit the windows. this resets the + --|maximized state during a reload problem is with our hack + --|to use maximized, we should look into using machi + --|resize_handler instead + client.connect_signal("request::activate", function(c) c.hidden = false c:raise() @@ -88,7 +110,8 @@ client.connect_signal("request::activate", function(c) end) --|this is needed to ensure floating stuff becomes --|visible when invoked through run_or_raise. -client.connect_signal("focus", function(c) +client.connect_signal("focus", function(c) +if not (c.bypass or c.always_on) then if not c.floating then for _, tc in ipairs(screen[awful.screen.focused()].all_clients) do if tc.floating and not tc.always_on then @@ -106,9 +129,14 @@ client.connect_signal("focus", function(c) end return end -end) --|hide all floating windows when the user switches to a - --|tiled client. this is handy when you have a floating - --|browser open. unless, client is set to always_on. +end +end) +--[[+] + hide all floating windows when the user switches to a tiled + client. This is handy when you have a floating browser + open. Unless, client is set to always_on or bypass through + rules. ]] + --------------------------------------------------------------- exports -- ; @@ -121,10 +149,4 @@ local function new(arg) return module end -return setmetatable(module, { __call = function(_,...) return new({...}) end }) - - --- return module -----------------╮ ---▨ FOCUS ▨ -----------------╯ \ No newline at end of file +return setmetatable(module, { __call = function(_,...) return new({...}) end }) \ No newline at end of file diff --git a/methods.lua b/methods.lua index f0f49e4..e272afb 100644 --- a/methods.lua +++ b/methods.lua @@ -1,86 +1,48 @@ +--------------------------------------------------------- dependencies -- ; + +local grect = require("gears.geometry").rectangle +local tabs = require("machina.tabs") +local geoms = require("machina.geoms") +local helpers = require("machina.helpers") + +local get_client_ix = helpers.get_client_ix +local getlowest = helpers.getlowest +local compare = helpers.compare +local tablelength = helpers.tablelength +local set_contains = helpers.set_contains +local clear_tabbar = helpers.clear_tabbar + ---------------------------------------------------------------- locals -- ; -local grect = require("gears.geometry").rectangle +local global_client_table = {} -local geoms = {} - -geoms.crt43 = function () - return { - x=awful.screen.focused().workarea.width - client.focus:geometry().width, - y=awful.screen.focused().workarea.height - client.focus:geometry().height, - width=1280, - height=1024 - } +function get_global_clients() + return global_client_table end -geoms.p1080 = function () - return { - x=awful.screen.focused().workarea.width - client.focus:geometry().width, - y=awful.screen.focused().workarea.height - client.focus:geometry().height, - width=awful.screen.focused().workarea.width * 0.65, - height=awful.screen.focused().workarea.height * 0.90 - } +function update_global_clients(c) + global_client_table[c.window] = c end -geoms["center"] = function(useless_gap) - return { - x=awful.screen.focused().workarea.width/2 - client.focus.width/2, - y=awful.screen.focused().workarea.height/2 - client.focus.height/2 - } -end +------------------------------------------------------------- go_edge() -- ; -geoms["top-left"] = function(useless_gap) - return { - x=useless_gap, - y=useless_gap - } -end +local function go_edge(direction, regions, current_box) + test_box = true + edge_pos = nil -geoms["bottom-left"] = function(useless_gap) - return { - x=useless_gap, - y=awful.screen.focused().workarea.height - useless_gap - client.focus.height - } -end + while test_box do + test_box = grect.get_in_direction(direction, regions, current_box) -geoms["top-right"] = function(useless_gap) - return { - x=awful.screen.focused().workarea.width - useless_gap - client.focus.width, - y=useless_gap - } -end - -geoms["bottom-right"] = function(useless_gap) - return { - x=awful.screen.focused().workarea.width - useless_gap - client.focus.width, - y=awful.screen.focused().workarea.height - useless_gap - client.focus.height - } -end - ---------------------------------------------------------------- helpers -- ; - -local function getlowest(table) - local low = math.huge - local index - for i, v in pairs(table) do - if v < low then - low = v - index = i + if test_box then + current_box = regions[test_box] + edge_pos = test_box end end - return index -end -local function compare(a,b) - return a.v < b.v -end - -local function tablelength(T) - local count = 0 - for _ in pairs(T) do count = count + 1 end - return count -end + return edge_pos +end --| + --|figures out the beginning of each row on the layout. ----------------------------------------------------------- always_on() -- ; @@ -89,16 +51,6 @@ local function toggle_always_on() client.focus.always_on = not always_on end ------------------------------------------ focus_by_direction(direction) -- ; - -local function focus_by_direction(direction) - return function() - if not client.focus then return false end - awful.client.focus.bydirection(direction, nil,true) - client.focus:raise() - end -end - --------------------------------------------------------- screen_info() -- ; local function screen_info() @@ -108,7 +60,11 @@ local function screen_info() local layout = awful.layout.get(focused_screen) or nil local focused_client = client.focus or nil - return focused_screen, workarea, selectedtag, layout, focused_client + return focused_screen, + workarea, + selected_tag, + layout, + focused_client end --------------------------------------------------------- get_regions() -- ; @@ -132,28 +88,21 @@ local function get_regions() if layout.machi_get_instance_data then machi_fn = layout.machi_get_instance_data - machi_data = {machi_fn(focused_screen, selected_tag)} + machi_data = {machi_fn(screen[focused_screen], selected_tag)} machi_regions = machi_data[3] for i=#machi_regions,1,-1 do - if machi_regions[i].habitable == false then - table.remove(machi_regions, i) - end + if machi_regions[i].habitable == false then + table.remove(machi_regions, i) + end end --|remove unhabitable regions table.sort( machi_regions, function (a1, a2) - local s1 = a1.width * a1.height - local s2 = a2.width * a2.height - if math.abs(s1 - s2) < 0.01 then - return (a1.x + a1.y) < (a2.x + a2.y) - else - return s1 > s2 - end + return a1.id > a2.id end - ) --|unlike v1, v2 returns unordered region list and - --|needs sorting + ) --|v2 returns unordered region list and needs sorting. end --|version 2/NG return machi_regions, machi_fn @@ -178,27 +127,30 @@ local function move_to(location) end end --------------------------------------------------- get_active_regions() -- ; +------------------------------------------------------- get_client_info -- ; -local function get_active_regions() +local function get_client_info(c) local active_region = nil local outofboundary = nil local proximity = {} local regions = get_regions() + local source_client = c or client.focus or nil - if (not regions) then return {} end + + if not regions then return {} end --|flow control - if not client.focus then return {} end + if not source_client then return {} end --|flow control - if client.focus.x < 0 or client.focus.y < 0 + if source_client.x < 0 or source_client.y < 0 then outofboundary = true end --| negative coordinates always mean out of boundary + for i, a in ipairs(regions) do - local px = a.x - client.focus.x - local py = a.y - client.focus.y + local px = a.x - source_client.x + local py = a.y - source_client.y if px == 0 then px = 1 end if py == 0 then py = 1 end @@ -215,13 +167,10 @@ local function get_active_regions() if not active_region then table.sort(proximity, compare) --| sort to get the smallest area active_region = proximity[1].index --| first item should be the right choice - ----┐ - -- naughty.notify({preset = naughty.config.presets.critical, text=inspect(regions[active_region])}) - -- naughty.notify({preset = naughty.config.presets.critical, text=tostring(client.focus.width .. " " .. client.focus.height)}) - if client.focus.floating then - if regions[active_region].width - client.focus.width ~= 2 - or regions[active_region].height - client.focus.height ~= 2 + if source_client.floating then + if regions[active_region].width - source_client.width ~= 0 + or regions[active_region].height - source_client.height ~= 0 then outofboundary = true end @@ -236,41 +185,67 @@ local function get_active_regions() end --|at this point, we are out of options, set the index --|to one and hope for the best. + + -- refactor + if active_region and source_client.width > regions[active_region].width then + outofboundary = true + end --|machi sometimes could auto expand the client, consider + --|that as out of boundary. + + if active_region and source_client.height > regions[active_region].height then + outofboundary = true + end --|machi sometimes could auto expand the client, consider + --|that as out of boundary. + -- refactor + + return { active_region = active_region, regions = regions, outofboundary = outofboundary } end ---|tablist order is adjusted by awesomewm and it will ---|always have the focused client as the first item. + +----------------------------------------- focus_by_direction(direction) -- ; + +local function focus_by_direction(direction) + return function() + if not client.focus then return false end + awful.client.focus.global_bydirection(direction, nil,true) + client.focus:raise() + end +end ------------------------------------------------------ region_tablist() -- ; -local function region_tablist() +local function region_tablist(region_ix, c) local focused_screen = awful.screen.focused() local workarea = awful.screen.focused().workarea local selected_tag = awful.screen.focused().selected_tag local tablist = {} - local active_region = nil + local active_region = region_ix or nil + local source_client = c or client.focus or nil local regions = get_regions() - if (not regions) then return {} end + if not regions then return {} end --|flow control - if not client.focus then return {} end + + ------------------ CHECK FOR SIDE EFFECTS + -- if not source_client or source_client.floating then return {} end --|flow control - if client.floating then return {} end - - for i, a in ipairs(regions) do - if a.x <= client.focus.x and client.focus.x < a.x + a.width and - a.y <= client.focus.y and client.focus.y < a.y + a.height - then - active_region = i + if not active_region then + for i, a in ipairs(regions) do + if a.x <= source_client.x and source_client.x < a.x + a.width and + a.y <= source_client.y and source_client.y < a.y + a.height + then + active_region = i + end end - end --|focused client's region + end --|if no region index provided, find the region of the + --|focused_client. for _, tc in ipairs(screen[focused_screen].tiled_clients) do if not (tc.floating or tc.immobilized) then @@ -283,12 +258,6 @@ local function region_tablist() end end end --|tablist inside the active region - - if tablelength(tablist) == 1 then - return {} - end --|flow control: if there is only one client in the - --|region, there is nothing to shuffle. having this here - --|makes it easier to avoid if nesting later. return tablist end @@ -309,11 +278,14 @@ local function expand_horizontal(direction) if c.direction == direction then c.direction = nil + c.maximized_horizontal = false + c.maximized_vertical = false + draw_tabbar(c.region) return end --|reset toggle when sending same shortcut --|consequitively - local stuff = get_active_regions() + local stuff = get_client_info() local target = grect.get_in_direction(direction, stuff.regions, client.focus:geometry()) if not target and direction ~= "center" then return end -- flow control @@ -322,7 +294,7 @@ local function expand_horizontal(direction) if direction == "right" then tobe = { x=c.x, - width=math.abs(stuff.regions[target].x + stuff.regions[target].width - c.x), + width=math.abs(stuff.regions[target].x + stuff.regions[target].width - c.x - 5), height=c.height, y=c.y } @@ -332,6 +304,8 @@ local function expand_horizontal(direction) gears.timer.delayed_call(function () client.focus:geometry(tobe) + draw_tabbar(c.region) + clear_tabbar(c) end) return end @@ -350,6 +324,8 @@ local function expand_horizontal(direction) gears.timer.delayed_call(function () client.focus:geometry(tobe) + draw_tabbar(c.region) + clear_tabbar(c) end) return end @@ -358,8 +334,10 @@ local function expand_horizontal(direction) if direction == "center" then c.maximized = false c.maximixed_vertical = false + fixedchoice = geoms.clients[c.class] or nil if c.floating then + c.maximized_horizontal = false geom = geoms.crt43() end @@ -368,11 +346,18 @@ local function expand_horizontal(direction) c.maximized_horizontal = true geom = geoms.p1080() end - + + if fixedchoice then + c.direction = "center" + geom = fixedchoice() + end + gears.timer.delayed_call(function () client.focus:geometry(geom) awful.placement.centered(client.focus) client.focus:raise() + draw_tabbar(c.region) + clear_tabbar(c) end) --|give it time in case maximize_horizontal is --|adjusted before centering return @@ -383,6 +368,9 @@ end --|tiled clients require an internal maximized property to --|be set, otherwise they won't budge. +--|change the logic handling for the center layout to use +--|fixedchoices + ----------------------------------------------------- expand_vertical() -- ; local function expand_vertical() @@ -394,7 +382,7 @@ local function expand_vertical() return end --|reset toggle maximized state - local stuff = get_active_regions() + local stuff = get_client_info() local target = grect.get_in_direction("down", stuff.regions, client.focus:geometry()) if target and stuff.regions[target].x ~= c.x then @@ -440,179 +428,385 @@ local function expand_vertical() return end ----------------------------------------------------- shift_by_direction -- ; - -local function shift_by_direction(direction, swap) - return function () - local stuff = get_active_regions() - local cltbl = awful.client.visible(client.focus.screen, true) - - local map = {} - for a,region in ipairs(stuff.regions) do - for i,c in ipairs(cltbl) do - if c.x == region.x and c.y == region.y then - map[a] = i - break --|avoid stacked regions - end - end - end --◸ - --|client list order we obtain via cltbl changes in - --|each invokation, therfore we need to map the - --|client table onto the region_list from machi. - --|this will give us the region numbers of clients. - --|naughty.notify({text=inspect(map)}) - --◺ - - local target = grect.get_in_direction(direction, stuff.regions, client.focus:geometry()) - --| awesomewm magic function to find out what lies - --| ahead and beyond based on the direction - - if not target then - target = stuff.active_region + 1 - - if target > #stuff.regions then - target = stuff.active_region - 1 - end - end --◸ - --|we bumped into an edge, try to locate region via - --|region_index and if that also fails, set back the - --|previous region as target clock wise. - --|naughty.notify({text=inspect(target)}) - --|naughty.notify({text=inspect(map[target])}) - --|naughty.notify({text=inspect(cltbl[map[target]])}) - --◺ - - tobe = stuff.regions[target] - is = client.focus:geometry() - - client.focus:geometry(tobe) - client.focus:raise() - --|relocate - - swapee = cltbl[map[target]] - --|try to get client at target region - - if swap and swapee then - swapee:geometry(is) - swapee:emit_signal("request::activate", "mouse_enter",{raise = true}) - end - - -- naughty.notify({text=inspect(cltbl[2]:geometry())}) - -- --◹◿ naughty.notify({text=inspect(cltbl[2]:geometry())}) - end -end - ------------------------------------------------------------- shuffle() -- ; local function shuffle(direction) return function() - if direction == "backward" then - local tablist = region_tablist() - local prev_client = nil + local tablist = get_tiled_clients() + --|this is the ordered list - for i = #tablist, 1, -1 do - prev_client = tablist[i] - prev_client:emit_signal("request::activate", "mouse_enter",{raise = true}) - break --|activate previous client - end + if not #tablist then return end + --▨ flow control + + if not client.focus then return end + --▨ flow control + + focused_client_ix = get_client_ix(client.focus.window, tablist) + --|find the index position of the focused client + + if not focused_client_ix then return end + --▨ flow control + + prev_ix = focused_client_ix - 1 + next_ix = focused_client_ix + 1 + --|calculate target indexes + + if next_ix > #tablist then next_ix = 1 end + if prev_ix < 1 then prev_ix = #tablist end + --|check for validity of the index + + if direction == "backward" then + tablist[prev_ix]:emit_signal("request::activate", "mouse_enter",{raise = true}) return end if direction == "forward" then - local tablist = region_tablist() - local next_client = nil - - for _, cc in ipairs(tablist) do - client.focus:lower() - next_client = tablist[_+1] - next_client:emit_signal("request::activate", "mouse_enter",{raise = true}) - break --|activate next client - end + tablist[next_ix]:emit_signal("request::activate", "mouse_enter",{raise = true}) return end end end -local function my_shifter(direction) +---------------------------------------------------------- get_swapee() -- ; + +local function get_swapee(target_region_ix) + local regions = get_regions() + --| all regions + + local cltbl = awful.client.visible(client.focus.screen, true) + --| all visible clients on all regions + --| but we don't know which regions they are at + + local swap_map = {} + + for a,region in ipairs(regions) do + for i,c in ipairs(cltbl) do + if c.x == region.x and c.y == region.y then + swap_map[a] = i + break --|avoid stacked regions + end + end + end --|iterate over regions, and match the client objects in + --|each region. + + local swapee = cltbl[swap_map[target_region_ix]] + return swapee +end +--[[ + returns the client object at a specific region. we can + also use signals to keep track of this but we are trying + to avoid exessive use of signals. +--]] + +---------------------------------------------------------- my_shifter() -- ; + +local function my_shifter(direction, swap) return function() + if direction == "left" then direction = "backward" end + if direction == "right" then direction = "forward" end + + local c = client.focus + local stuff = get_client_info() + local client_region_ix = stuff.active_region + local source_region + local target_region_ix + local target_region + if direction == "backward" then - local next_client = nil - local stuff = get_active_regions() - local client_region = stuff.active_region - local next_region - - if (client_region + 1 > #stuff.regions) then - next_region=stuff.regions[1] + if (client_region_ix + 1) > #stuff.regions then + target_region_ix = 1 else - next_region=stuff.regions[client_region+1] - end --|figure out the action - - if stuff.outofboundary then - next_region=stuff.regions[client_region] - end --|ignore action, and push inside the boundary instead - - client.focus:geometry({ - x=next_region.x, - y=next_region.y, - width=next_region.width-2, - height=next_region.height-2 - }) - return - end + target_region_ix = client_region_ix+1 + end + end --|go next region by index, + --|if not reset to first if direction == "forward" then - - local next_client = nil - local stuff = get_active_regions() - local client_region = stuff.active_region - local previous_region - - if (client_region - 1 < 1) then - previous_region = stuff.regions[#stuff.regions] + if (client_region_ix - 1) < 1 then + target_region_ix = #stuff.regions else - previous_region = stuff.regions[client_region-1] - end --|figure out the action + target_region_ix = client_region_ix - 1 + end + end --|go previous region by index, + --|if not reset to last - if stuff.outofboundary then - previous_region = stuff.regions[client_region] - end --|ignore action, and push inside the boundary instead + if stuff.outofboundary then + target_region_ix = client_region_ix + end --|ignore previous when out of boundary + --|probably floating or expanded client + --|push inside the boundary instead - client.focus:geometry({ - x=previous_region.x, - y=previous_region.y, - width=previous_region.width-2, - height=previous_region.height-2 - }) + source_region = stuff.regions[client_region_ix] + target_region = stuff.regions[target_region_ix] + --|target regions geometry + + local swapee = get_swapee(target_region_ix) + --|visible client at the target region + + c:geometry(target_region) + --|relocate client + + if not swap then c:raise() end + --|raise + + if swap and swapee then + swapee:geometry(source_region) + swapee:emit_signal("request::activate", "mouse_enter",{raise = true}) + end --|perform swap + + draw_tabbar(target_region_ix) + --|update tabs in target region - end - - + draw_tabbar(client_region_ix) + --|update tabs in source region end end +---------------------------------------------------- shift_by_direction -- ; + +local function shift_by_direction(direction, swap) + return function () + + local c = client.focus + local stuff = get_client_info() + local target_region_ix = nil + local client_region_ix = stuff.active_region + + if stuff.outofboundary == true then + return my_shifter(direction)() + end --|my_shifter handles this situation better. + + local candidate = { + up = grect.get_in_direction("up", stuff.regions, client.focus:geometry()), + down = grect.get_in_direction("down", stuff.regions, client.focus:geometry()), + left = grect.get_in_direction("left", stuff.regions, client.focus:geometry()), + right = grect.get_in_direction("right", stuff.regions, client.focus:geometry()) + } --|awesomewm magic function to find out what lies + --|ahead and beyond based on the direction + + target_region_ix = candidate[direction] + --|try to get a candidate region if possible + + if not target_region_ix then + if direction == "right" then try = "left" end + if direction == "left" then try = "right" end + if direction == "down" then try = "up" end + if direction == "up" then try = "down" end + + target_region_ix = go_edge(try, stuff.regions, client.focus:geometry()) + end --|go the beginning or the end if there is no + --|candidate + + + source_region = stuff.regions[client_region_ix] + target_region = stuff.regions[target_region_ix] + + local swapee = get_swapee(target_region_ix) + --|visible client at the target region + + c:geometry(target_region) + --|relocate client + + if not swap then c:raise() end + --|raise + + if swap and swapee then + swapee:geometry(source_region) + swapee:emit_signal("request::activate", "mouse_enter",{raise = true}) + end --|perform swap + + draw_tabbar(target_region_ix) + --|update tabs in target region + + draw_tabbar(client_region_ix) + --|update tabs in source region + end +end + +----------------------------------------------------- get_tiled_clients -- ; + +function get_tiled_clients(region_ix) + local tablist = region_tablist(region_ix) + + local all_clients = get_global_clients() + local tiled_clients = {} + local myorder = {} + local window_ix = {} + + for i,t in ipairs(tablist) do + window_ix[t.window] = true + end + + local po = 1 + for i,c in pairs(all_clients) do + if not c.floating and window_ix[c.window] then + tiled_clients[po] = c + po = po + 1 + end + end + + return tiled_clients +end +--[[+ + global_client_index stores the ordered list of all clients + available and it is used as a blueprint to keep the order + of our tablist intact, without this, tabbars would go out + of order when user focuses via shortcuts (run_or_raise). ]] + +-------------------------------------------------------- draw_tabbar() -- ; + +function draw_tabbar(region_ix) + local flexlist = tabs.layout() + local tablist = get_tiled_clients(region_ix) + + if tablelength(tablist) == 0 then + return + end --|this should only fire on an empty region + + if tablelength(tablist) == 1 then + clear_tabbar(tablist[1]) + return + end --|reset tabbar titlebar when only + --|one client is in the region. + + for c_ix, c in ipairs(tablist) do + local flexlist = tabs.layout() + + for cc_ix, cc in ipairs(tablist) do + local buttons = gears.table.join(awful.button({}, 1, function() end)) + wid_temp = tabs.create(cc, (cc == c), buttons, c_ix) + flexlist:add(wid_temp) + end + + local titlebar = awful.titlebar(c, { + bg = tabs.bg_normal, + size = tabs.size, + position = tabs.position, + }) + + titlebar:setup{layout = wibox.layout.flex.horizontal, flexlist} + awful.titlebar(c, {size=8, position = "top"}) + awful.titlebar(c, {size=0, position = "left"}) + awful.titlebar(c, {size=0, position = "right"}) + end +end + + +------------------------------------------------------ signal helpers -- ; + +local function manage_signal(c) + if c then + global_client_table[c.window] = c + --|add window.id to global index + + local active_region = get_client_info(c).active_region + if active_region then + c.region = active_region + draw_tabbar(active_region) + end --|in case new client appears tiled + --|we must update the regions tabbars. + end +end + +local function unmanage_signal(c) + if c then + global_client_table[c.window] = nil + --|remove window.id to global index + + if not c.floating then + local active_region = get_client_info(c).active_region + if active_region then + draw_tabbar(active_region) end + end + end +end + +local function selected_tag_signal(t) + gears.timer.delayed_call(function() + local regions = get_regions() + if regions and #regions then + for i, region in ipairs(regions) do + draw_tabbar(i) + end + end + end) +end + +local function floating_signal(c) + if c.floating then + if c.region then + clear_tabbar(c) + draw_tabbar(c.region) + end + end --|window became floating + + if not c.floating then + local active_region = get_client_info(c).active_region + if active_region then + c.region = active_region + gears.timer.delayed_call(function(active_region) + draw_tabbar(active_region) + end, active_region) + end + end --|window became tiled +end + +--------------------------------------------------------------- signals -- ; + +client.connect_signal("property::minimized", function(c) + if c.minimized then unmanage_signal(c) end + if not c.minimized then manage_signal(c) end +end) +--[[+] manage minimized and not minimized ]] + + +client.connect_signal("property::floating", floating_signal) +--[[+] + when windows switch between float and tiled we must + perform necessary maintenance on the destination and + source regions. a delayed call was necessary when + clients become tiled to give awm enough time to draw the + widgets properly.]] + +client.connect_signal("unmanage", unmanage_signal) +--[[+] + when removing a tiled client we must update the tabbars + of others. floating clients do not require any cleanup. ]] + +client.connect_signal("manage", manage_signal) +--[[+] + global_client_table is the milestone the tabbars rely on. + whenever a new client appears we must add to it, and + when a client is killed we must make sure it is removed. ]] + +tag.connect_signal("property::selected", selected_tag_signal) +--[[+] + property::selected gets called the last, by the time we + are here, we already have the global_client_list to draw + tabbars. This may appear redundant here, but it's good + to have this fire up in case the user switches tags. ]] + --------------------------------------------------------------- exports -- ; module = { - region_tablist = region_tablist, focus_by_direction = focus_by_direction, - compare = compare, - get_active_regions = get_active_regions, + get_active_regions = get_client_info, shift_by_direction = shift_by_direction, expand_horizontal = expand_horizontal, - geoms = geoms, shuffle = shuffle, + old_shuffle = old_shuffle, my_shifter = my_shifter, expand_vertical = expand_vertical, move_to = move_to, - toggle_always_on = toggle_always_on + get_regions = get_regions, + toggle_always_on = toggle_always_on, + draw_tabbar = draw_tabbar, + get_global_clients = get_global_clients, + update_global_clients = update_global_clients, + get_client_info = get_client_info, } return module - - --- naughty.notify({preset = naughty.config.presets.critical,text=inspect(client.focus:geometry())}) --- naughty.notify({preset = naughty.config.presets.critical,text=inspect(regions)}) --- naughty.notify({preset = naughty.config.presets.critical,text=inspect(proximity)}) diff --git a/tabs.lua b/tabs.lua new file mode 100644 index 0000000..2536e9b --- /dev/null +++ b/tabs.lua @@ -0,0 +1,61 @@ + +--------------------------------------------- widget.tabbar.default.lua -- ; + +local gears = require("gears") +local wibox = require("wibox") + +local beautiful = require("beautiful") + +local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#1a1a1a" +local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#595959" +local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#292929" +local bg_active = "#43417a" +-- local bg_active = "#394037" + +local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff" +local font = beautiful.tabbar_font or beautiful.font or "Recursive Sans Casual Static 8" +local size = beautiful.tabbar_size or 14 +local position = beautiful.tabbar_position or "bottom" + + +local function create(c, focused_bool, buttons, idx) + local flexlist = wibox.layout.flex.horizontal + local title_temp = string.lower(c.class) or c.name or "-" + local bg_temp = bg_normal + local fg_temp = fg_normal + + if focused_bool then + bg_temp = bg_active + fg_temp = fg_focus + end + + local text_temp = wibox.widget.textbox() + text_temp.align = "center" + text_temp.valign = "center" + text_temp.wrap = "word" + text_temp.font = font + text_temp.focused = false + text_temp.markup = "" .. title_temp.. "" + + if focused_bool then text_temp.focused = true end + + local wid_temp = wibox.widget({ + id = c.window..idx, + text_temp, + buttons = buttons, + bg = bg_temp, + widget = wibox.container.background() + }) + + return wid_temp +end + +return { + layout = wibox.layout.flex.horizontal, + create = create, + create_focused = create_focused, + position = position, + size = size, + bg_normal = bg_normal, + bg_focus = bg_focus, +} \ No newline at end of file diff --git a/tabs_modern.lua b/tabs_modern.lua new file mode 100644 index 0000000..025a673 --- /dev/null +++ b/tabs_modern.lua @@ -0,0 +1,206 @@ +--------------------------------------------- widget.tabbar.default.lua -- ; + +local awful = require("awful") +local gears = require("gears") +local wibox = require("wibox") +local beautiful = require("beautiful") +local xresources = require("beautiful.xresources") +local dpi = xresources.apply_dpi +local helpers = require("bling.helpers") + +local bg_normal = beautiful.tabbar_bg_normal or beautiful.bg_normal or "#ffffff" +local fg_normal = beautiful.tabbar_fg_normal or beautiful.fg_normal or "#000000" +local bg_focus = beautiful.tabbar_bg_focus or beautiful.bg_focus or "#000000" +local fg_focus = beautiful.tabbar_fg_focus or beautiful.fg_focus or "#ffffff" +local font = beautiful.tabbar_font or beautiful.font or "Recursive Sans Casual Static 9" +local size = beautiful.tabbar_size or 30 or dpi(40) +local border_radius = + beautiful.mstab_border_radius or beautiful.border_radius or 4 +local position = beautiful.tabbar_position or "bottom" +local close_color = beautiful.tabbar_color_close or beautiful.xcolor1 or + "#f9929b" +local min_color = beautiful.tabbar_color_min or beautiful.xcolor3 or "#fbdf90" +local float_color = beautiful.tabbar_color_float or beautiful.xcolor5 or + "#ccaced" + +-- Helper to create buttons +local function create_title_button(c, color_focus, color_unfocus) + local tb_color = wibox.widget { + wibox.widget.textbox(), + forced_width = dpi(8), + forced_height = dpi(8), + bg = color_focus, + shape = gears.shape.circle, + widget = wibox.container.background + } + + local tb = wibox.widget { + tb_color, + width = dpi(25), + height = dpi(25), + strategy = "min", + layout = wibox.layout.constraint + } + + local function update() + if client.focus == c then + tb_color.bg = color_focus + else + tb_color.bg = color_unfocus + end + end + update() + c:connect_signal("focus", update) + c:connect_signal("unfocus", update) + + tb:connect_signal("mouse::enter", + function() tb_color.bg = color_focus .. "70" end) + + tb:connect_signal("mouse::leave", function() tb_color.bg = color_focus end) + + tb.visible = true + return tb +end + +local function create(c, focused_bool, buttons) + -- local flexlist = wibox.layout.flex.horizontal() + local title_temp = c.class or c.name or "-" + local bg_temp = bg_normal + local fg_temp = fg_normal + if focused_bool then + bg_temp = bg_focus + fg_temp = fg_focus + end + local text_temp = wibox.widget.textbox() + text_temp.align = "center" + text_temp.valign = "center" + text_temp.font = font + text_temp.markup = "" .. title_temp .. + "" + c:connect_signal("property::name", function(_) + local title_temp = c.name or c.class or "-" + text_temp.markup = + "" .. title_temp .. "" + end) + + local tab_content = wibox.widget { + { + awful.widget.clienticon(c), + top = dpi(6), + left = dpi(15), + bottom = dpi(6), + widget = wibox.container.margin + }, + text_temp, + nill, + expand = "none", + layout = wibox.layout.align.horizontal + } + + if focused_bool then + tab_content = wibox.widget { + { + awful.widget.clienticon(c), + top = dpi(6), + left = dpi(15), + bottom = dpi(6), + widget = wibox.container.margin + }, + text_temp, + nil, + expand = "none", + layout = wibox.layout.align.horizontal + } + end + + local main_content = nil + local left_shape = nil + local right_shape = nil + + if position == "top" then + main_content = wibox.widget { + { + tab_content, + bg = bg_temp, + shape = helpers.shape.prrect(border_radius, true, true, false, + false), + widget = wibox.container.background + }, + top = dpi(8), + widget = wibox.container.margin + } + + left_shape = helpers.shape.prrect(border_radius, false, false, true, + false) + right_shape = helpers.shape.prrect(border_radius, false, false, false, + true) + else + main_content = wibox.widget { + { + tab_content, + bg = bg_temp, + shape = helpers.shape.prrect(border_radius, false, false, true, + true), + widget = wibox.container.background + }, + bottom = dpi(8), + widget = wibox.container.margin + } + + left_shape = helpers.shape.prrect(border_radius, false, true, false, + false) + right_shape = helpers.shape.prrect(border_radius, true, false, false, + false) + end + + local wid_temp = wibox.widget({ + buttons = buttons, + { + { + { + wibox.widget.textbox(), + bg = bg_normal, + shape = left_shape, + widget = wibox.container.background + }, + bg = bg_temp, + shape = gears.rectangle, + widget = wibox.container.background + }, + width = border_radius + (border_radius / 2), + height = size, + strategy = "exact", + layout = wibox.layout.constraint + }, + main_content, + { + { + { + wibox.widget.textbox(), + bg = bg_normal, + shape = right_shape, + widget = wibox.container.background + }, + bg = bg_temp, + shape = gears.rectangle, + widget = wibox.container.background + }, + width = border_radius + (border_radius / 2), + height = size, + strategy = "exact", + layout = wibox.layout.constraint + }, + + layout = wibox.layout.align.horizontal + }) + return wid_temp +end + +return { + layout = wibox.layout.flex.horizontal, + create = create, + position = position, + size = size, + bg_normal = bg_normal, + bg_focus = bg_focus +}