-
Notifications
You must be signed in to change notification settings - Fork 11
/
focus.lua
273 lines (243 loc) · 9.44 KB
/
focus.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
local capi = { client = client , mouse = mouse ,
screen = screen , keygrabber = keygrabber,}
local setmetatable = setmetatable
local ipairs = ipairs
local surface = require( "gears.surface" )
local shape = require( "gears.shape" )
local util = require( "awful.util" )
local client = require( "awful.client" )
local tag = require( "awful.tag" )
local alayout = require( "awful.layout" )
local wibox = require( "wibox" )
local cairo = require( "lgi" ).cairo
local beautiful = require( "beautiful" )
local color = require( "gears.color" )
local col_utils = require( "collision.util" )
local grect = require( "gears.geometry" ).rectangle
local placement = require( "awful.placement")
local module = {}
local wiboxes,delta = nil,100
local edge = nil
---------------- Visual -----------------------
local function init()
wiboxes = {}
local s = beautiful.collision_focus_shape or shape.rounded_rect
local bw = beautiful.collision_focus_border_width
local bc = beautiful.collision_focus_border_color
local padding = beautiful.collision_focus_padding or 7
local bg = beautiful.collision_focus_bg or beautiful.bg_alternate or "#ff0000"
local fg = beautiful.collision_focus_fg or beautiful.fg_normal or "#0000ff"
local bg_focus = beautiful.collision_focus_bg_center or beautiful.bg_urgent or "#ff0000"
local sw = beautiful.collision_shape_width or 75
local sh = beautiful.collision_shape_height or 75
local arrow_bc = beautiful.collision_focus_arrow_border_color
local arrow_bw = beautiful.collision_focus_arrow_border_width or 0
local cshape = beautiful.collision_focus_shape_center or shape.circle
for k,v in ipairs({"up","right","down","left","center"}) do
wiboxes[v] = wibox {
height = sh,
width = sw,
ontop = true
}
local r_shape = v == "center" and cshape or s
local r_bg = v == "center" and bg_focus or bg
local x = (sw - arrow_bw*2)/2 - padding
local y = (sh - arrow_bw*2)/2 - padding
wiboxes[v]:setup {
v ~= "center" and {
{
{
widget = wibox.widget.imagebox
},
bg = fg,
border_color = arrow_bc,
border_width = arrow_bw,
widget = wibox.container.background,
shape = shape.transform(col_utils.arrow_path2)
: rotate_at(x, y, (k-1)*(2*math.pi)/4),
},
margins = padding,
widget = wibox.container.margin,
} or {
widget = wibox.widget.imagebox
},
bg = r_bg,
shape = r_shape,
shape_border_width = bw,
shape_border_color = bc,
widget = wibox.container.background
}
if awesome.version >= "v4.1" then
wiboxes[v].shape = r_shape
else
surface.apply_shape_bounding(wiboxes[v], r_shape)
end
end
end
local function emulate_client(screen)
return {is_screen = true, screen=screen, geometry=function() return capi.screen[screen].workarea end}
end
local function display_wiboxes(cltbl,geomtbl,float,swap,c)
if not wiboxes then
init()
end
local fc = capi.client.focus or emulate_client(capi.mouse.screen)
for k,v in ipairs({"left","right","up","down","center"}) do
local next_clients = (float and swap) and c or cltbl[grect.get_in_direction(v , geomtbl, fc:geometry())]
if next_clients or k==5 then
local parent = k==5 and fc or next_clients
wiboxes[v].visible = true
placement.centered(wiboxes[v], {parent = parent})
else
wiboxes[v].visible = false
end
end
end
---------------- Position -----------------------
local function float_move(dir,c)
return ({left={x=c:geometry().x-delta},right={x=c:geometry().x+delta},up={y=c:geometry().y-delta},down={y=c:geometry().y+delta}})[dir]
end
local function float_move_max(dir,c)
return ({left={x=capi.screen[c.screen].workarea.x},right={x=capi.screen[c.screen].workarea.width+capi.screen[c.screen].workarea.x-c:geometry().width}
,up={y=capi.screen[c.screen].workarea.y},down={y=capi.screen[c.screen].workarea.y+capi.screen[c.screen].workarea.height-c:geometry().height}})[dir]
end
local function floating_clients()
local ret = {}
for v in util.table.iterate(client.visible(),function(c) return c.floating end) do
ret[#ret+1] = v
end
return ret
end
local function bydirection(dir, c, swap,max)
if not c then
c = emulate_client(capi.mouse.screen)
end
local float = nil
if c.is_screen then
float = false
else
float = (c.floating or alayout.get(c.screen) == alayout.suit.floating)
end
-- Move the client if floating, swaping wont work anyway
if swap and float then
c:geometry((max and float_move_max or float_move)(dir,c))
display_wiboxes(nil,nil,float,swap,c)
else
if not edge then
local scrs =col_utils.get_ordered_screens()
local last_geo =capi.screen[scrs[#scrs]].geometry
edge = last_geo.x + last_geo.width
end
-- Get all clients rectangle
local cltbl,geomtbl,scrs,roundr,roundl = max and floating_clients() or client.tiled(),{},{},{},{}
for i,cl in ipairs(cltbl) do
local geo = cl:geometry()
geomtbl[i] = geo
scrs[capi.screen[cl.screen or 1]] = true
if geo.x == 0 then
roundr[#roundr+1] = cl
elseif geo.x + geo.width >= edge -2 then
roundl[#roundl+1] = cl
end
end
--Add first client at the end to be able to rotate selection
for k,c in ipairs(roundr) do
local geo = c:geometry()
geomtbl[#geomtbl+1] = {x=edge,width=geo.width,y=geo.y,height=geo.height}
cltbl[#geomtbl] = c
end
for k,c in ipairs(roundl) do
local geo = c:geometry()
geomtbl[#geomtbl+1] = {x=-geo.width,width=geo.width,y=geo.y,height=geo.height}
cltbl[#geomtbl] = c
end
-- Add rectangles for empty screens too
for i = 1, capi.screen.count() do
if not scrs[capi.screen[i]] then
geomtbl[#geomtbl+1] = capi.screen[i].workarea
cltbl[#geomtbl] = emulate_client(i)
end
end
local target = grect.get_in_direction(dir, geomtbl, c:geometry())
if swap ~= true then
-- If we found a client to focus, then do it.
if target then
local cl = cltbl[target]
if cl and cl.is_screen then
capi.client.focus = nil --TODO Fix upstream fix
capi.mouse.screen = capi.screen[cl.screen]
else
local old_src = capi.client.focus and capi.client.focus.screen
capi.client.focus = cltbl[((not cl and #cltbl == 1) and 1 or target)]
capi.client.focus:raise()
if not old_src or capi.client.focus.screen ~= capi.screen[old_src] then
capi.mouse.coords(capi.client.focus:geometry())
end
end
end
else
if target then
-- We found a client to swap
local other = cltbl[((not cltbl[target] and #cltbl == 1) and 1 or target)]
if capi.screen[other.screen] == capi.screen[c.screen] or col_utils.settings.swap_across_screen then
--BUG swap doesn't work if the screen is not the same
c:swap(other)
else
local t = capi.screen[other.screen].selected_tag --TODO get index
c.screen = capi.screen[ other.screen]
c:tags({t})
end
else
-- No client to swap, try to find a screen.
local screen_geom = {}
for i = 1, capi.screen.count() do
screen_geom[i] = capi.screen[i].workarea
end
target = grect.get_in_direction(dir, screen_geom, c:geometry())
if target and target ~= c.screen then
local t = capi.screen[target].selected_tag
c.screen = target
c:tags({t})
c:raise()
end
end
if target then
-- Geometries have changed by swapping, so refresh.
cltbl,geomtbl = max and floating_clients() or client.tiled(),{}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
end
end
display_wiboxes(cltbl,geomtbl,float,swap,c)
end
end
function module.global_bydirection(dir, c,swap,max)
bydirection(dir, c or capi.client.focus, swap,max)
end
function module._global_bydirection_key(mod,key,event,direction,is_swap,is_max)
bydirection(direction,capi.client.focus,is_swap,is_max)
return true
end
function module.display(mod,key,event,direction,is_swap,is_max)
local c = capi.client.focus
local cltbl,geomtbl = max and floating_clients() or client.tiled(),{}
for i,cl in ipairs(cltbl) do
geomtbl[i] = cl:geometry()
end
-- Sometime, there is no focussed clients
if not c then
c = geomtbl[1] or cltbl[1]
end
-- If there is still no accessible clients, there is nothing to display
if not c then return end
display_wiboxes(cltbl,geomtbl,c.floating or alayout.get(c.screen) == alayout.suit.floating,is_swap,c)
end
function module._quit()
if not wiboxes then return end
for k,v in ipairs({"left","right","up","down","center"}) do
wiboxes[v].visible = false
end
end
return setmetatable(module, { __call = function(_, ...) return new(...) end })
-- kate: space-indent on; indent-width 4; replace-tabs on;