2023-09-06 05:13:43 +02:00
|
|
|
---Rules that apply to spawned windows when conditions are met.
|
|
|
|
---@class WindowRules
|
|
|
|
local window_rules = {}
|
|
|
|
|
2023-09-08 03:36:49 +02:00
|
|
|
---Convert all tag constructors in `cond` to tag ids for serialization.
|
2023-09-06 06:18:44 +02:00
|
|
|
---@param cond WindowRuleCondition
|
|
|
|
---@return _WindowRuleCondition
|
|
|
|
local function convert_tag_params(cond)
|
|
|
|
if cond.tag then
|
2023-09-08 03:36:49 +02:00
|
|
|
local tags = {}
|
|
|
|
|
|
|
|
if type(cond.tag) == "table" then
|
|
|
|
if cond.tag.name or cond.tag.output then
|
|
|
|
-- Tag constructor
|
|
|
|
local tag = require("tag").get(cond.tag)
|
|
|
|
if tag then
|
|
|
|
table.insert(tags, tag:id())
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Array of tag constructors
|
|
|
|
---@diagnostic disable-next-line
|
|
|
|
for _, t in pairs(cond.tag) do
|
|
|
|
local tag = require("tag").get(t)
|
|
|
|
if tag then
|
|
|
|
table.insert(tags, tag:id())
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Tag constructor
|
|
|
|
local tag = require("tag").get(cond.tag)
|
|
|
|
if tag then
|
|
|
|
table.insert(tags, tag:id())
|
|
|
|
end
|
2023-09-06 06:18:44 +02:00
|
|
|
end
|
2023-09-08 03:36:49 +02:00
|
|
|
|
|
|
|
cond.tag = tags
|
2023-09-06 06:18:44 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if cond.cond_any then
|
|
|
|
local conds = {}
|
2023-09-08 03:36:49 +02:00
|
|
|
if type(cond.cond_any[1]) == "table" then
|
|
|
|
-- Array of conds
|
|
|
|
for _, c in pairs(cond.cond_any) do
|
|
|
|
table.insert(conds, convert_tag_params(c))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Single cond
|
|
|
|
table.insert(conds, convert_tag_params(cond.cond_any))
|
2023-09-06 06:18:44 +02:00
|
|
|
end
|
|
|
|
cond.cond_any = conds
|
|
|
|
end
|
|
|
|
|
|
|
|
if cond.cond_all then
|
|
|
|
local conds = {}
|
2023-09-08 03:36:49 +02:00
|
|
|
if type(cond.cond_all[1]) == "table" then
|
|
|
|
-- Array of conds
|
|
|
|
for _, c in pairs(cond.cond_all) do
|
|
|
|
table.insert(conds, convert_tag_params(c))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Single cond
|
|
|
|
table.insert(conds, convert_tag_params(cond.cond_all))
|
2023-09-06 06:18:44 +02:00
|
|
|
end
|
|
|
|
cond.cond_all = conds
|
|
|
|
end
|
|
|
|
|
|
|
|
return cond --[[@as _WindowRuleCondition]]
|
|
|
|
end
|
|
|
|
|
2023-09-08 03:36:49 +02:00
|
|
|
---These attributes need to be arrays, so this function converts single values into arrays.
|
|
|
|
---@param cond WindowRuleCondition
|
|
|
|
---@return WindowRuleCondition
|
|
|
|
local function convert_single_attrs(cond)
|
|
|
|
if type(cond.class) == "string" then
|
|
|
|
-- stylua: ignore start
|
|
|
|
cond.class = { cond.class --[[@as string]] }
|
|
|
|
-- stylua: ignore end
|
|
|
|
end
|
|
|
|
|
|
|
|
if type(cond.title) == "string" then
|
|
|
|
-- stylua: ignore start
|
|
|
|
cond.title = { cond.title --[[@as string]] }
|
|
|
|
-- stylua: ignore end
|
|
|
|
end
|
|
|
|
|
|
|
|
if cond.cond_any then
|
|
|
|
local conds = {}
|
|
|
|
if type(cond.cond_any[1]) == "table" then
|
|
|
|
-- Array of conds
|
|
|
|
for _, c in pairs(cond.cond_any) do
|
|
|
|
table.insert(conds, convert_single_attrs(c))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Single cond
|
|
|
|
table.insert(conds, convert_single_attrs(cond.cond_any))
|
|
|
|
end
|
|
|
|
cond.cond_any = conds
|
|
|
|
end
|
|
|
|
|
|
|
|
if cond.cond_all then
|
|
|
|
local conds = {}
|
|
|
|
if type(cond.cond_all[1]) == "table" then
|
|
|
|
-- Array of conds
|
|
|
|
for _, c in pairs(cond.cond_all) do
|
|
|
|
table.insert(conds, convert_single_attrs(c))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- Single cond
|
|
|
|
table.insert(conds, convert_single_attrs(cond.cond_all))
|
|
|
|
end
|
|
|
|
cond.cond_all = conds
|
|
|
|
end
|
|
|
|
|
|
|
|
return cond
|
|
|
|
end
|
|
|
|
|
2023-09-06 05:13:43 +02:00
|
|
|
---Add one or more window rules.
|
|
|
|
---
|
2023-09-08 03:48:41 +02:00
|
|
|
---A window rule defines what properties a window will spawn with given certain conditions.
|
|
|
|
---For example, if Firefox is spawned, you can set it to open on a specific tag.
|
2023-09-06 05:13:43 +02:00
|
|
|
---
|
|
|
|
---This function takes in a table with two keys:
|
2023-09-08 03:48:41 +02:00
|
|
|
---
|
2023-09-06 05:13:43 +02:00
|
|
|
--- - `cond`: The condition for `rule` to apply to a new window.
|
|
|
|
--- - `rule`: What gets applied to the new window if `cond` is true.
|
|
|
|
---
|
2023-09-08 03:36:49 +02:00
|
|
|
---There are some important mechanics you should know when using window rules:
|
|
|
|
---
|
|
|
|
--- - All children inside a `cond_all` block must be true for the block to be true.
|
|
|
|
--- - At least one child inside a `cond_any` block must be true for the block to be true.
|
|
|
|
--- - The outermost block of a window rule condition is implicitly a `cond_all` block.
|
|
|
|
--- - All condition attributes (`tag`, `title`, `class`, etc.) can either be a single value or an array.
|
|
|
|
--- This includes `cond_all` and `cond_any`.
|
2023-09-08 03:48:41 +02:00
|
|
|
--- - Within a `cond_all` block, any arrays must have all items be true for the attribute to be true.
|
|
|
|
--- - Within a `cond_any` block, any arrays only need one item to be true for the attribute to be true.
|
2023-09-08 03:36:49 +02:00
|
|
|
---
|
|
|
|
---`cond` can be a bit confusing and quite table heavy. Examples are shown below for guidance.
|
2023-09-06 05:13:43 +02:00
|
|
|
---
|
|
|
|
---### Examples
|
|
|
|
---```lua
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- A simple window rule. This one will cause Firefox to open on tag "Browser".
|
2023-09-06 05:13:43 +02:00
|
|
|
---window.rules.add({
|
|
|
|
--- cond = { class = "firefox" },
|
|
|
|
--- rule = { tags = { "Browser" } },
|
|
|
|
---})
|
|
|
|
---
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- To apply rules when *all* provided conditions are true, use `cond_all`.
|
|
|
|
--- -- `cond_all` takes an array of conditions and checks if all are true.
|
|
|
|
--- -- The following will open Steam fullscreen only if it opens on tag "5".
|
2023-09-06 05:13:43 +02:00
|
|
|
---window.rules.add({
|
|
|
|
--- cond = {
|
2023-09-08 03:36:49 +02:00
|
|
|
--- cond_all = {
|
|
|
|
--- class = "steam",
|
|
|
|
--- tag = tag.get("5"),
|
2023-09-06 05:13:43 +02:00
|
|
|
--- }
|
|
|
|
--- },
|
|
|
|
--- rule = { fullscreen_or_maximized = "Fullscreen" },
|
|
|
|
---})
|
|
|
|
---
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- The outermost block of a `cond` is implicitly a `cond_all`.
|
|
|
|
--- -- Thus, the above can be shortened to:
|
2023-09-08 03:36:49 +02:00
|
|
|
---window.rules.add({
|
|
|
|
--- cond = {
|
|
|
|
--- class = "steam",
|
|
|
|
--- tag = tag.get("5"),
|
|
|
|
--- },
|
|
|
|
--- rule = { fullscreen_or_maximized = "Fullscreen" },
|
|
|
|
---})
|
|
|
|
---
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- `cond_any` also exists to allow at least one provided condition to match.
|
|
|
|
--- -- The following will open either xterm or Alacritty floating.
|
2023-09-08 03:36:49 +02:00
|
|
|
---window.rules.add({
|
|
|
|
--- cond = {
|
|
|
|
--- cond_any = { class = { "xterm", "Alacritty" } }
|
|
|
|
--- },
|
|
|
|
--- rule = { floating_or_tiled = "Floating" }
|
|
|
|
---})
|
|
|
|
---
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- You can arbitrarily nest `cond_any` and `cond_all` to achieve desired logic.
|
|
|
|
--- -- The following will open Discord, Thunderbird, or Firefox floating if they
|
|
|
|
--- -- open on either *all* of tags "A", "B", and "C" or both tags "1" and "2".
|
2023-09-06 05:13:43 +02:00
|
|
|
---window.rules.add({
|
2023-09-09 03:27:03 +02:00
|
|
|
--- cond = { cond_all = { -- This `cond_all` block is needed because the outermost block cannot be an array.
|
2023-09-08 03:36:49 +02:00
|
|
|
--- { cond_any = { class = { "firefox", "thunderbird", "discord" } } },
|
2023-09-06 05:13:43 +02:00
|
|
|
--- { cond_any = {
|
2023-09-08 03:48:41 +02:00
|
|
|
--- -- Because `tag` is inside a `cond_all` block,
|
|
|
|
--- -- the window must have all these tags for this to be true.
|
|
|
|
--- -- If it was in a `cond_any` block, only one tag would need to match.
|
2023-09-08 03:36:49 +02:00
|
|
|
--- { cond_all = { tag = { "A", "B", "C" } } },
|
|
|
|
--- { cond_all = { tag = { "1", "2" } } },
|
2023-09-06 05:13:43 +02:00
|
|
|
--- } }
|
|
|
|
--- } },
|
|
|
|
--- rule = { floating_or_tiled = "Floating" },
|
|
|
|
---})
|
|
|
|
---```
|
|
|
|
---@param ... { cond: WindowRuleCondition, rule: WindowRule }
|
|
|
|
function window_rules.add(...)
|
|
|
|
local rules = { ... }
|
|
|
|
|
|
|
|
for _, rule in pairs(rules) do
|
2023-09-08 03:36:49 +02:00
|
|
|
rule.cond = convert_single_attrs(rule.cond)
|
|
|
|
|
2023-09-08 00:42:03 +02:00
|
|
|
---@diagnostic disable-next-line
|
2023-09-06 06:18:44 +02:00
|
|
|
rule.cond = convert_tag_params(rule.cond)
|
|
|
|
|
|
|
|
if rule.rule.tags then
|
|
|
|
local tags = {}
|
|
|
|
for _, tag in pairs(rule.rule.tags) do
|
2023-09-08 03:36:49 +02:00
|
|
|
local t = require("tag").get(tag)
|
2023-09-06 06:18:44 +02:00
|
|
|
if t then
|
|
|
|
---@diagnostic disable-next-line
|
|
|
|
t = t:id()
|
|
|
|
end
|
|
|
|
table.insert(tags, t)
|
|
|
|
end
|
|
|
|
rule.rule.tags = tags
|
|
|
|
end
|
|
|
|
|
2023-09-08 00:42:03 +02:00
|
|
|
if rule.rule.output and type(rule.rule.output) == "table" then
|
|
|
|
rule.rule.output = rule
|
|
|
|
.rule
|
|
|
|
.output--[[@as Output]]
|
|
|
|
:name()
|
|
|
|
end
|
2023-09-06 06:18:44 +02:00
|
|
|
|
2023-09-06 05:13:43 +02:00
|
|
|
SendMsg({
|
|
|
|
AddWindowRule = {
|
2023-09-06 06:18:44 +02:00
|
|
|
-- stylua: ignore start
|
|
|
|
cond = rule.cond --[[@as _WindowRuleCondition]],
|
|
|
|
rule = rule.rule --[[@as _WindowRule]],
|
|
|
|
-- stylua: ignore end
|
2023-09-06 05:13:43 +02:00
|
|
|
},
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return window_rules
|