summaryrefslogtreecommitdiff
path: root/awesome/lib/awful/layout/init.lua
blob: dfce5629144b55f7c3d756a3650e88cab848dd59 (plain)
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
---------------------------------------------------------------------------
--- Layout module for awful
--
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2008 Julien Danjou
-- @module awful.layout
---------------------------------------------------------------------------

-- Grab environment we need
local ipairs = ipairs
local type = type
local util = require("awful.util")
local capi = {
    screen = screen,
    mouse  = mouse,
    awesome = awesome,
    client = client,
    tag = tag
}
local tag = require("awful.tag")
local client = require("awful.client")
local ascreen = require("awful.screen")
local timer = require("gears.timer")

local function get_screen(s)
    return s and capi.screen[s]
end

local layout = {}

layout.suit = require("awful.layout.suit")

layout.layouts = {
    layout.suit.floating,
    layout.suit.tile,
    layout.suit.tile.left,
    layout.suit.tile.bottom,
    layout.suit.tile.top,
    layout.suit.fair,
    layout.suit.fair.horizontal,
    layout.suit.spiral,
    layout.suit.spiral.dwindle,
    layout.suit.max,
    layout.suit.max.fullscreen,
    layout.suit.magnifier,
    layout.suit.corner.nw,
    layout.suit.corner.ne,
    layout.suit.corner.sw,
    layout.suit.corner.se,
}

--- The default list of layouts.
--
-- The default value is:
--
--    awful.layout.suit.floating,
--    awful.layout.suit.tile,
--    awful.layout.suit.tile.left,
--    awful.layout.suit.tile.bottom,
--    awful.layout.suit.tile.top,
--    awful.layout.suit.fair,
--    awful.layout.suit.fair.horizontal,
--    awful.layout.suit.spiral,
--    awful.layout.suit.spiral.dwindle,
--    awful.layout.suit.max,
--    awful.layout.suit.max.fullscreen,
--    awful.layout.suit.magnifier,
--    awful.layout.suit.corner.nw,
--    awful.layout.suit.corner.ne,
--    awful.layout.suit.corner.sw,
--    awful.layout.suit.corner.se,
--
-- @field layout.layouts


-- This is a special lock used by the arrange function.
-- This avoids recurring call by emitted signals.
local arrange_lock = false
-- Delay one arrange call per screen.
local delayed_arrange = {}

--- Get the current layout.
-- @param screen The screen.
-- @return The layout function.
function layout.get(screen)
    screen = screen or capi.mouse.screen
    local t = get_screen(screen).selected_tag
    return tag.getproperty(t, "layout") or layout.suit.floating
end

--- Change the layout of the current tag.
-- @param i Relative index.
-- @param s The screen.
-- @param[opt] layouts A table of layouts.
function layout.inc(i, s, layouts)
    if type(i) == "table" then
        -- Older versions of this function had arguments (layouts, i, s), but
        -- this was changed so that 'layouts' can be an optional parameter
        layouts, i, s = i, s, layouts
    end
    s = get_screen(s or ascreen.focused())
    local t = s.selected_tag
    layouts = layouts or layout.layouts
    if t then
        local curlayout = layout.get(s)
        local curindex
        for k, v in ipairs(layouts) do
            if v == curlayout or curlayout._type == v then
                curindex = k
                break
            end
        end
        if not curindex then
            -- Safety net: handle cases where another reference of the layout
            -- might be given (e.g. when (accidentally) cloning it).
            for k, v in ipairs(layouts) do
                if v.name == curlayout.name then
                    curindex = k
                    break
                end
            end
        end
        if curindex then
            local newindex = util.cycle(#layouts, curindex + i)
            layout.set(layouts[newindex], t)
        end
    end
end

--- Set the layout function of the current tag.
-- @param _layout Layout name.
-- @tparam[opt=mouse.screen.selected_tag] tag t The tag to modify.
function layout.set(_layout, t)
    t = t or capi.mouse.screen.selected_tag
    t.layout = _layout
end

--- Get the layout parameters used for the screen
--
-- This should give the same result as "arrange", but without the "geometries"
-- parameter, as this is computed during arranging.
--
-- If `t` is given, `screen` is ignored, if none are given, the mouse screen is
-- used.
--
-- @tparam[opt] tag t The tag to query
-- @param[opt] screen The screen
-- @treturn table A table with the workarea (x, y, width, height), the screen
--   geometry (x, y, width, height), the clients, the screen and sometime, a
--   "geometries" table with client as keys and geometry as value
function layout.parameters(t, screen)
    screen = get_screen(screen)
    t = t or screen.selected_tag

    screen = get_screen(t and t.screen or 1)

    local p = {}

    local clients           = client.tiled(screen)
    local gap_single_client = true

    if(t and t.gap_single_client ~= nil) then
        gap_single_client = t.gap_single_client
    end

    local min_clients       = gap_single_client and 1 or 2
    local useless_gap       = t and (#clients >= min_clients and t.gap or 0) or 0

    p.workarea = screen:get_bounding_geometry {
        honor_padding  = true,
        honor_workarea = true,
        margins        = useless_gap,
    }

    p.geometry    = screen.geometry
    p.clients     = clients
    p.screen      = screen.index
    p.padding     = screen.padding
    p.useless_gap = useless_gap

    return p
end

--- Arrange a screen using its current layout.
-- @param screen The screen to arrange.
function layout.arrange(screen)
    screen = get_screen(screen)
    if not screen or delayed_arrange[screen] then return end
    delayed_arrange[screen] = true

    timer.delayed_call(function()
        if not screen.valid then
            -- Screen was removed
            delayed_arrange[screen] = nil
            return
        end
        if arrange_lock then return end
        arrange_lock = true

        local p = layout.parameters(nil, screen)

        local useless_gap = p.useless_gap

        p.geometries = setmetatable({}, {__mode = "k"})
        layout.get(screen).arrange(p)
        for c, g in pairs(p.geometries) do
            g.width = math.max(1, g.width - c.border_width * 2 - useless_gap * 2)
            g.height = math.max(1, g.height - c.border_width * 2 - useless_gap * 2)
            g.x = g.x + useless_gap
            g.y = g.y + useless_gap
            c:geometry(g)
        end
        arrange_lock = false
        delayed_arrange[screen] = nil

        screen:emit_signal("arrange")
    end)
end

--- Get the current layout name.
-- @param _layout The layout.
-- @return The layout name.
function layout.getname(_layout)
    _layout = _layout or layout.get()
    return _layout.name
end

local function arrange_prop_nf(obj)
    if not client.object.get_floating(obj) then
        layout.arrange(obj.screen)
    end
end

local function arrange_prop(obj) layout.arrange(obj.screen) end

capi.client.connect_signal("property::size_hints_honor", arrange_prop_nf)
capi.client.connect_signal("property::struts", arrange_prop)
capi.client.connect_signal("property::minimized", arrange_prop_nf)
capi.client.connect_signal("property::sticky", arrange_prop_nf)
capi.client.connect_signal("property::fullscreen", arrange_prop_nf)
capi.client.connect_signal("property::maximized_horizontal", arrange_prop_nf)
capi.client.connect_signal("property::maximized_vertical", arrange_prop_nf)
capi.client.connect_signal("property::border_width", arrange_prop_nf)
capi.client.connect_signal("property::hidden", arrange_prop_nf)
capi.client.connect_signal("property::floating", arrange_prop)
capi.client.connect_signal("property::geometry", arrange_prop_nf)
capi.client.connect_signal("property::screen", function(c, old_screen)
    if old_screen then
        layout.arrange(old_screen)
    end
    layout.arrange(c.screen)
end)

local function arrange_tag(t)
    layout.arrange(t.screen)
end

capi.tag.connect_signal("property::master_width_factor", arrange_tag)
capi.tag.connect_signal("property::master_count", arrange_tag)
capi.tag.connect_signal("property::column_count", arrange_tag)
capi.tag.connect_signal("property::layout", arrange_tag)
capi.tag.connect_signal("property::windowfact", arrange_tag)
capi.tag.connect_signal("property::selected", arrange_tag)
capi.tag.connect_signal("property::activated", arrange_tag)
capi.tag.connect_signal("property::useless_gap", arrange_tag)
capi.tag.connect_signal("property::master_fill_policy", arrange_tag)
capi.tag.connect_signal("tagged", arrange_tag)

capi.screen.connect_signal("property::workarea", layout.arrange)
capi.screen.connect_signal("padding", layout.arrange)

capi.client.connect_signal("raised", function(c) layout.arrange(c.screen) end)
capi.client.connect_signal("lowered", function(c) layout.arrange(c.screen) end)
capi.client.connect_signal("list", function()
                                   for screen in capi.screen do
                                       layout.arrange(screen)
                                   end
                               end)

--- Default handler for `request::geometry` signals for tiled clients with
-- the "mouse.move" context.
-- @tparam client c The client
-- @tparam string context The context
-- @tparam table hints Additional hints
function layout.move_handler(c, context, hints) --luacheck: no unused args
    -- Quit if it isn't a mouse.move on a tiled layout, that's handled elsewhere
    if c.floating then return end
    if context ~= "mouse.move" then return end

    if capi.mouse.screen ~= c.screen then
        c.screen = capi.mouse.screen
    end

    local l = c.screen.selected_tag and c.screen.selected_tag.layout or nil
    if l == layout.suit.floating then return end

    local c_u_m = capi.mouse.current_client
    if c_u_m and not c_u_m.floating then
        if c_u_m ~= c then
            c:swap(c_u_m)
        end
    end
end

capi.client.connect_signal("request::geometry", layout.move_handler)

-- When a screen is moved, make (floating) clients follow it
capi.screen.connect_signal("property::geometry", function(s, old_geom)
    local geom = s.geometry
    local xshift = geom.x - old_geom.x
    local yshift = geom.y - old_geom.y
    for _, c in ipairs(capi.client.get(s)) do
        local cgeom = c:geometry()
        c:geometry({
            x = cgeom.x + xshift,
            y = cgeom.y + yshift
        })
    end
end)

return layout

-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80