mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Simplify window rules API
This commit is contained in:
parent
2eb69326cd
commit
d76cb13a1f
7 changed files with 333 additions and 175 deletions
|
@ -90,7 +90,7 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
|
|
||||||
op:add_tags("1", "2", "3", "4", "5")
|
op:add_tags("1", "2", "3", "4", "5")
|
||||||
-- Same as tag.add(op, "1", "2", "3", "4", "5")
|
-- Same as tag.add(op, "1", "2", "3", "4", "5")
|
||||||
tag.toggle({ "1", op })
|
tag.toggle({ name = "1", output = op })
|
||||||
|
|
||||||
-- Window rules
|
-- Window rules
|
||||||
window.rules.add({
|
window.rules.add({
|
||||||
|
@ -98,9 +98,27 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
rule = { size = { 300, 300 }, location = { 50, 50 } },
|
rule = { size = { 300, 300 }, location = { 50, 50 } },
|
||||||
}, {
|
}, {
|
||||||
cond = {
|
cond = {
|
||||||
cond_all = { { class = "XTerm" }, { tag = "4" } },
|
class = "XTerm",
|
||||||
|
tag = "4",
|
||||||
},
|
},
|
||||||
rule = { size = { 500, 500 }, floating_or_tiled = "Floating" },
|
rule = { size = { 500, 800 }, floating_or_tiled = "Floating" },
|
||||||
|
})
|
||||||
|
|
||||||
|
window.rules.add({
|
||||||
|
cond = {
|
||||||
|
cond_all = {
|
||||||
|
class = "Alacritty",
|
||||||
|
cond_any = {
|
||||||
|
{
|
||||||
|
cond_all = {
|
||||||
|
tag = { "3", "4", "5" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ cond_all = { tag = { "1", "2" } } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rule = { floating_or_tiled = "Floating" },
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
166
api/lua/tag.lua
166
api/lua/tag.lua
|
@ -41,8 +41,9 @@ local tag_module = {}
|
||||||
---| "CornerBottomLeft" # One main corner window in the bottom left with a column of windows on the right and a row on the top.
|
---| "CornerBottomLeft" # One main corner window in the bottom left with a column of windows on the right and a row on the top.
|
||||||
---| "CornerBottomRight" # One main corner window in the bottom right with a column of windows on the left and a row on the top.
|
---| "CornerBottomRight" # One main corner window in the bottom right with a column of windows on the left and a row on the top.
|
||||||
|
|
||||||
---@alias TagTable { [1]: string, [2]: (string|Output)? }
|
---@alias TagTable { name: string, output: (string|Output)? }
|
||||||
---@alias TagTableNamed { name: string, output: (string|Output)? }
|
|
||||||
|
---@alias TagConstructor Tag|TagTable|string
|
||||||
|
|
||||||
---A tag object.
|
---A tag object.
|
||||||
---
|
---
|
||||||
|
@ -52,92 +53,6 @@ local tag_module = {}
|
||||||
---@field private _id integer The internal id of this tag.
|
---@field private _id integer The internal id of this tag.
|
||||||
local tag = {}
|
local tag = {}
|
||||||
|
|
||||||
---@nodoc
|
|
||||||
---***You probably don't need to use this function.***
|
|
||||||
---
|
|
||||||
---Create a tag from `Tag|TagTable|TagTableNamed|string`.
|
|
||||||
---@param tb Tag|TagTable|TagTableNamed|string
|
|
||||||
---@return Tag|nil
|
|
||||||
function tag_module.create_tag_from_params(tb)
|
|
||||||
-- If creating from a tag object, just return the obj
|
|
||||||
if tb.id then
|
|
||||||
return tb --[[@as Tag]]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- string passed in
|
|
||||||
if type(tb) == "string" then
|
|
||||||
local op = require("output").get_focused()
|
|
||||||
if op == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local tags = tag_module.get_by_name(tb)
|
|
||||||
for _, t in pairs(tags) do
|
|
||||||
if t:output() and t:output():name() == op:name() then
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TagTable was passed in
|
|
||||||
local tag_name = tb[1]
|
|
||||||
if type(tag_name) == "string" then
|
|
||||||
local op = tb[2]
|
|
||||||
if op == nil then
|
|
||||||
local o = require("output").get_focused()
|
|
||||||
if o == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
op = o
|
|
||||||
elseif type(op) == "string" then
|
|
||||||
local o = require("output").get_by_name(op)
|
|
||||||
if o == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
op = o
|
|
||||||
end
|
|
||||||
|
|
||||||
local tags = tag_module.get_by_name(tag_name)
|
|
||||||
for _, t in pairs(tags) do
|
|
||||||
if t:output() and t:output():name() == op:name() then
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TagTableNamed was passed in
|
|
||||||
local tb = tb --[[@as TagTableNamed]]
|
|
||||||
local tag_name = tb.name
|
|
||||||
local op = tb.output
|
|
||||||
|
|
||||||
if op == nil then
|
|
||||||
local o = require("output").get_focused()
|
|
||||||
if o == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
op = o
|
|
||||||
elseif type(op) == "string" then
|
|
||||||
local o = require("output").get_by_name(op)
|
|
||||||
if o == nil then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
op = o
|
|
||||||
end
|
|
||||||
|
|
||||||
local tags = tag_module.get_by_name(tag_name)
|
|
||||||
for _, t in pairs(tags) do
|
|
||||||
if t:output() and t:output():name() == op:name() then
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---Create a tag from an id.
|
---Create a tag from an id.
|
||||||
---The id is the unique identifier for each tag.
|
---The id is the unique identifier for each tag.
|
||||||
---@param id TagId
|
---@param id TagId
|
||||||
|
@ -265,10 +180,10 @@ end
|
||||||
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
||||||
---tag.toggle(t)
|
---tag.toggle(t)
|
||||||
---```
|
---```
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see Tag.toggle — The corresponding object method
|
---@see Tag.toggle — The corresponding object method
|
||||||
function tag_module.toggle(t)
|
function tag_module.toggle(t)
|
||||||
local t = tag_module.create_tag_from_params(t)
|
local t = tag_module.get(t)
|
||||||
|
|
||||||
if t then
|
if t then
|
||||||
SendMsg({
|
SendMsg({
|
||||||
|
@ -302,10 +217,10 @@ end
|
||||||
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
||||||
---tag.switch_to(t)
|
---tag.switch_to(t)
|
||||||
---```
|
---```
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see Tag.switch_to — The corresponding object method
|
---@see Tag.switch_to — The corresponding object method
|
||||||
function tag_module.switch_to(t)
|
function tag_module.switch_to(t)
|
||||||
local t = tag_module.create_tag_from_params(t)
|
local t = tag_module.get(t)
|
||||||
|
|
||||||
if t then
|
if t then
|
||||||
SendMsg({
|
SendMsg({
|
||||||
|
@ -323,25 +238,20 @@ end
|
||||||
---local op = output.get_by_name("DP-1")
|
---local op = output.get_by_name("DP-1")
|
||||||
---
|
---
|
||||||
---tag.set_layout("1", "Dwindle") -- Set tag 1 on the focused output to "Dwindle"
|
---tag.set_layout("1", "Dwindle") -- Set tag 1 on the focused output to "Dwindle"
|
||||||
---tag.set_layout({ "1" }, "Dwindle") -- Same as above
|
|
||||||
---
|
---
|
||||||
---tag.set_layout({ "1", "DP-1" }, "Dwindle") -- Set tag 1 on DP-1 to "Dwindle"
|
---tag.set_layout({ name = "1", output = "DP-1" }, "Dwindle") -- Set tag 1 on "DP-1" to "Dwindle"
|
||||||
---tag.set_layout({ "1", op }, "Dwindle") -- Same as above
|
---tag.set_layout({ name = "1", output = op }, "Dwindle") -- Same as above
|
||||||
---
|
|
||||||
----- Verbose versions of the two above
|
|
||||||
---tag.set_layout({ name = "1", output = "DP-1" }, "Dwindle")
|
|
||||||
---tag.set_layout({ name = "1", output = op }, "Dwindle")
|
|
||||||
---
|
---
|
||||||
----- Using a tag object
|
----- Using a tag object
|
||||||
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
||||||
---tag.set_layout(t, "Dwindle")
|
---tag.set_layout(t, "Dwindle")
|
||||||
---```
|
---```
|
||||||
---
|
---
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@param layout Layout The layout.
|
---@param layout Layout The layout.
|
||||||
---@see Tag.set_layout — The corresponding object method
|
---@see Tag.set_layout — The corresponding object method
|
||||||
function tag_module.set_layout(t, layout)
|
function tag_module.set_layout(t, layout)
|
||||||
local t = tag_module.create_tag_from_params(t)
|
local t = tag_module.get(t)
|
||||||
|
|
||||||
if t then
|
if t then
|
||||||
SendMsg({
|
SendMsg({
|
||||||
|
@ -364,22 +274,70 @@ end
|
||||||
---### Examples
|
---### Examples
|
||||||
---```lua
|
---```lua
|
||||||
---local t = tag.get("1")
|
---local t = tag.get("1")
|
||||||
---local t = tag.get({ "1", "HDMI-A-0" })
|
|
||||||
---local t = tag.get({ name = "3" })
|
---local t = tag.get({ name = "3" })
|
||||||
|
---local t = tag.get({ name = "1", output = "HDMI-A-0" })
|
||||||
---
|
---
|
||||||
---local op = output.get_by_name("DP-2")
|
---local op = output.get_by_name("DP-2")
|
||||||
---if op ~= nil then
|
---if op ~= nil then
|
||||||
--- local t = tag.get({ name = "Code", output = op })
|
--- local t = tag.get({ name = "Code", output = op })
|
||||||
---end
|
---end
|
||||||
---```
|
---```
|
||||||
---@param params TagTable|TagTableNamed|string
|
---@param params TagConstructor
|
||||||
---@return Tag|nil
|
---@return Tag|nil
|
||||||
---
|
---
|
||||||
---@see TagModule.get_on_output
|
---@see TagModule.get_on_output
|
||||||
---@see TagModule.get_by_name
|
---@see TagModule.get_by_name
|
||||||
---@see TagModule.get_all
|
---@see TagModule.get_all
|
||||||
function tag_module.get(params)
|
function tag_module.get(params)
|
||||||
return tag_module.create_tag_from_params(params)
|
-- If creating from a tag object, just return the obj
|
||||||
|
if params.id then
|
||||||
|
return params --[[@as Tag]]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- string passed in
|
||||||
|
if type(params) == "string" then
|
||||||
|
local op = require("output").get_focused()
|
||||||
|
if op == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = tag_module.get_by_name(params)
|
||||||
|
for _, t in pairs(tags) do
|
||||||
|
if t:output() and t:output():name() == op:name() then
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TagTable was passed in
|
||||||
|
local params = params --[[@as TagTable]]
|
||||||
|
local tag_name = params.name
|
||||||
|
local op = params.output
|
||||||
|
|
||||||
|
if op == nil then
|
||||||
|
local o = require("output").get_focused()
|
||||||
|
if o == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
op = o
|
||||||
|
elseif type(op) == "string" then
|
||||||
|
local o = require("output").get_by_name(op)
|
||||||
|
if o == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
op = o
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = tag_module.get_by_name(tag_name)
|
||||||
|
for _, t in pairs(tags) do
|
||||||
|
if t:output() and t:output():name() == op:name() then
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get all tags on the specified output.
|
---Get all tags on the specified output.
|
||||||
|
|
|
@ -55,7 +55,7 @@ end
|
||||||
---
|
---
|
||||||
---See `WindowModule.move_to_tag` for examples.
|
---See `WindowModule.move_to_tag` for examples.
|
||||||
---
|
---
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see WindowModule.move_to_tag — The corresponding module function
|
---@see WindowModule.move_to_tag — The corresponding module function
|
||||||
function window:move_to_tag(t)
|
function window:move_to_tag(t)
|
||||||
window_module.move_to_tag(self, t)
|
window_module.move_to_tag(self, t)
|
||||||
|
@ -66,7 +66,7 @@ end
|
||||||
---Note: toggling off all tags currently makes a window not respond to layouting.
|
---Note: toggling off all tags currently makes a window not respond to layouting.
|
||||||
---
|
---
|
||||||
---See `WindowModule.toggle_tag` for examples.
|
---See `WindowModule.toggle_tag` for examples.
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see WindowModule.toggle_tag — The corresponding module function
|
---@see WindowModule.toggle_tag — The corresponding module function
|
||||||
function window:toggle_tag(t)
|
function window:toggle_tag(t)
|
||||||
window_module.toggle_tag(self, t)
|
window_module.toggle_tag(self, t)
|
||||||
|
@ -254,10 +254,10 @@ end
|
||||||
---Toggle the tag with the given name and (optional) output for the specified window.
|
---Toggle the tag with the given name and (optional) output for the specified window.
|
||||||
---
|
---
|
||||||
---@param w Window
|
---@param w Window
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see Window.toggle_tag — The corresponding object method
|
---@see Window.toggle_tag — The corresponding object method
|
||||||
function window_module.toggle_tag(w, t)
|
function window_module.toggle_tag(w, t)
|
||||||
local t = require("tag").create_tag_from_params(t)
|
local t = require("tag").get(t)
|
||||||
|
|
||||||
if t then
|
if t then
|
||||||
SendMsg({
|
SendMsg({
|
||||||
|
@ -272,10 +272,10 @@ end
|
||||||
---Move the specified window to the tag with the given name and (optional) output.
|
---Move the specified window to the tag with the given name and (optional) output.
|
||||||
---
|
---
|
||||||
---@param w Window
|
---@param w Window
|
||||||
---@param t Tag|TagTable|TagTableNamed|string
|
---@param t TagConstructor
|
||||||
---@see Window.move_to_tag — The corresponding object method
|
---@see Window.move_to_tag — The corresponding object method
|
||||||
function window_module.move_to_tag(w, t)
|
function window_module.move_to_tag(w, t)
|
||||||
local t = require("tag").create_tag_from_params(t)
|
local t = require("tag").get(t)
|
||||||
|
|
||||||
if t then
|
if t then
|
||||||
SendMsg({
|
SendMsg({
|
||||||
|
|
|
@ -2,33 +2,65 @@
|
||||||
---@class WindowRules
|
---@class WindowRules
|
||||||
local window_rules = {}
|
local window_rules = {}
|
||||||
|
|
||||||
---Convert all tag constructors in `cond` to actual tags
|
---Convert all tag constructors in `cond` to tag ids for serialization.
|
||||||
---@param cond WindowRuleCondition
|
---@param cond WindowRuleCondition
|
||||||
---@return _WindowRuleCondition
|
---@return _WindowRuleCondition
|
||||||
local function convert_tag_params(cond)
|
local function convert_tag_params(cond)
|
||||||
if cond.tag then
|
if cond.tag then
|
||||||
---@type TagId|Tag|nil
|
local tags = {}
|
||||||
local tag = require("tag").create_tag_from_params(cond.tag)
|
|
||||||
if tag then
|
if type(cond.tag) == "table" then
|
||||||
---@diagnostic disable-next-line
|
if cond.tag.name or cond.tag.output then
|
||||||
tag = tag:id()
|
-- 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
|
||||||
end
|
end
|
||||||
---@diagnostic disable-next-line
|
|
||||||
cond.tag = tag
|
cond.tag = tags
|
||||||
end
|
end
|
||||||
|
|
||||||
if cond.cond_any then
|
if cond.cond_any then
|
||||||
local conds = {}
|
local conds = {}
|
||||||
for _, c in pairs(cond.cond_any) do
|
if type(cond.cond_any[1]) == "table" then
|
||||||
table.insert(conds, convert_tag_params(c))
|
-- 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))
|
||||||
end
|
end
|
||||||
cond.cond_any = conds
|
cond.cond_any = conds
|
||||||
end
|
end
|
||||||
|
|
||||||
if cond.cond_all then
|
if cond.cond_all then
|
||||||
local conds = {}
|
local conds = {}
|
||||||
for _, c in pairs(cond.cond_all) do
|
if type(cond.cond_all[1]) == "table" then
|
||||||
table.insert(conds, convert_tag_params(c))
|
-- 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))
|
||||||
end
|
end
|
||||||
cond.cond_all = conds
|
cond.cond_all = conds
|
||||||
end
|
end
|
||||||
|
@ -36,6 +68,53 @@ local function convert_tag_params(cond)
|
||||||
return cond --[[@as _WindowRuleCondition]]
|
return cond --[[@as _WindowRuleCondition]]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---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
|
||||||
|
|
||||||
---Add one or more window rules.
|
---Add one or more window rules.
|
||||||
---
|
---
|
||||||
---A window rule defines what a window will spawn with given certain conditions.
|
---A window rule defines what a window will spawn with given certain conditions.
|
||||||
|
@ -45,8 +124,15 @@ end
|
||||||
--- - `cond`: The condition for `rule` to apply to a new window.
|
--- - `cond`: The condition for `rule` to apply to a new window.
|
||||||
--- - `rule`: What gets applied to the new window if `cond` is true.
|
--- - `rule`: What gets applied to the new window if `cond` is true.
|
||||||
---
|
---
|
||||||
---`cond` can be a bit confusing and *very* table heavy. Examples are shown below for guidance.
|
---There are some important mechanics you should know when using window rules:
|
||||||
---An attempt at simplifying this API will happen in the future, but is a low priority.
|
---
|
||||||
|
--- - 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`.
|
||||||
|
---
|
||||||
|
---`cond` can be a bit confusing and quite table heavy. Examples are shown below for guidance.
|
||||||
---
|
---
|
||||||
---### Examples
|
---### Examples
|
||||||
---```lua
|
---```lua
|
||||||
|
@ -58,28 +144,45 @@ end
|
||||||
---
|
---
|
||||||
----- To apply rules when *all* provided conditions are true, use `cond_all`.
|
----- 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.
|
----- `cond_all` takes an array of conditions and checks if all are true.
|
||||||
----- Note that `cond_any` is not a keyed table; rather, it's a table of tables.
|
|
||||||
---
|
|
||||||
----- The following will open Steam fullscreen only if it opens on tag "5".
|
----- The following will open Steam fullscreen only if it opens on tag "5".
|
||||||
---window.rules.add({
|
---window.rules.add({
|
||||||
--- cond = {
|
--- cond = {
|
||||||
--- cond_any = {
|
--- cond_all = {
|
||||||
--- { class = "steam" }, -- Note that each table must only have one key.
|
--- class = "steam",
|
||||||
--- { tag = tag.get_by_name("5")[1] },
|
--- tag = tag.get("5"),
|
||||||
--- }
|
--- }
|
||||||
--- },
|
--- },
|
||||||
--- rule = { fullscreen_or_maximized = "Fullscreen" },
|
--- rule = { fullscreen_or_maximized = "Fullscreen" },
|
||||||
---})
|
---})
|
||||||
---
|
---
|
||||||
|
----- The outermost block of a `cond` is implicitly a `cond_all`.
|
||||||
|
----- Thus, the above can be shortened to:
|
||||||
|
---window.rules.add({
|
||||||
|
--- cond = {
|
||||||
|
--- class = "steam",
|
||||||
|
--- tag = tag.get("5"),
|
||||||
|
--- },
|
||||||
|
--- rule = { fullscreen_or_maximized = "Fullscreen" },
|
||||||
|
---})
|
||||||
|
---
|
||||||
|
----- `cond_any` also exists to allow at least one provided condition to match.
|
||||||
|
----- The following will open either xterm or Alacritty floating.
|
||||||
|
---window.rules.add({
|
||||||
|
--- cond = {
|
||||||
|
--- cond_any = { class = { "xterm", "Alacritty" } }
|
||||||
|
--- },
|
||||||
|
--- rule = { floating_or_tiled = "Floating" }
|
||||||
|
---})
|
||||||
|
---
|
||||||
----- You can arbitrarily nest `cond_any` and `cond_all` to achieve desired logic.
|
----- You can arbitrarily nest `cond_any` and `cond_all` to achieve desired logic.
|
||||||
----- The following will open Discord, Thunderbird, or Alacritty floating if they
|
----- 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".
|
----- open on either *all* of tags "A", "B", and "C" or both tags "1" and "2".
|
||||||
---window.rules.add({
|
---window.rules.add({
|
||||||
--- cond = { cond_all = {
|
--- cond = { cond_all = { -- This outer `cond_all` block is unnecessary, but it's here for clarity.
|
||||||
--- { cond_any = { { class = "discord" }, { class = "firefox" }, { class = "thunderbird" } } },
|
--- { cond_any = { class = { "firefox", "thunderbird", "discord" } } },
|
||||||
--- { cond_any = {
|
--- { cond_any = {
|
||||||
--- { cond_all = { { tag = "A" }, { tag = "B" }, { tag = "C" } } },
|
--- { cond_all = { tag = { "A", "B", "C" } } },
|
||||||
--- { cond_all = { { tag = "1" }, { tag = "2" } } },
|
--- { cond_all = { tag = { "1", "2" } } },
|
||||||
--- } }
|
--- } }
|
||||||
--- } },
|
--- } },
|
||||||
--- rule = { floating_or_tiled = "Floating" },
|
--- rule = { floating_or_tiled = "Floating" },
|
||||||
|
@ -90,13 +193,15 @@ function window_rules.add(...)
|
||||||
local rules = { ... }
|
local rules = { ... }
|
||||||
|
|
||||||
for _, rule in pairs(rules) do
|
for _, rule in pairs(rules) do
|
||||||
|
rule.cond = convert_single_attrs(rule.cond)
|
||||||
|
|
||||||
---@diagnostic disable-next-line
|
---@diagnostic disable-next-line
|
||||||
rule.cond = convert_tag_params(rule.cond)
|
rule.cond = convert_tag_params(rule.cond)
|
||||||
|
|
||||||
if rule.rule.tags then
|
if rule.rule.tags then
|
||||||
local tags = {}
|
local tags = {}
|
||||||
for _, tag in pairs(rule.rule.tags) do
|
for _, tag in pairs(rule.rule.tags) do
|
||||||
local t = require("tag").create_tag_from_params(tag)
|
local t = require("tag").get(tag)
|
||||||
if t then
|
if t then
|
||||||
---@diagnostic disable-next-line
|
---@diagnostic disable-next-line
|
||||||
t = t:id()
|
t = t:id()
|
||||||
|
|
|
@ -8,19 +8,19 @@
|
||||||
---@class _WindowRuleCondition
|
---@class _WindowRuleCondition
|
||||||
---@field cond_any _WindowRuleCondition[]? At least one provided condition must be true.
|
---@field cond_any _WindowRuleCondition[]? At least one provided condition must be true.
|
||||||
---@field cond_all _WindowRuleCondition[]? All provided conditions must be true.
|
---@field cond_all _WindowRuleCondition[]? All provided conditions must be true.
|
||||||
---@field class string? The window must have this class.
|
---@field class string[]? The window must have this class.
|
||||||
---@field title string? The window must have this title.
|
---@field title string[]? The window must have this title.
|
||||||
---@field tag TagId? The window must be on this tag.
|
---@field tag TagId[]? The window must be on this tag.
|
||||||
|
|
||||||
---Conditions for window rules. Only one condition can be in the table.
|
---Conditions for window rules. Only one condition can be in the table.
|
||||||
---If you have more than one you need to check for, use `cond_any` or `cond_all`
|
---If you have more than one you need to check for, use `cond_any` or `cond_all`
|
||||||
---to check for any or all conditions.
|
---to check for any or all conditions.
|
||||||
---@class WindowRuleCondition
|
---@class WindowRuleCondition
|
||||||
---@field cond_any WindowRuleCondition[]? At least one provided condition must be true.
|
---@field cond_any (WindowRuleCondition|WindowRuleCondition[])? At least one provided condition must be true.
|
||||||
---@field cond_all WindowRuleCondition[]? All provided conditions must be true.
|
---@field cond_all (WindowRuleCondition|WindowRuleCondition[])? All provided conditions must be true.
|
||||||
---@field class string? The window must have this class.
|
---@field class (string|string[])? The window must have this class.
|
||||||
---@field title string? The window must have this title.
|
---@field title (string|string[])? The window must have this title.
|
||||||
---@field tag (Tag|TagTable|TagTableNamed|string)? The window must be on this tag.
|
---@field tag (TagConstructor|TagConstructor[])? The window must be on this tag.
|
||||||
|
|
||||||
---@class _WindowRule Attributes the window will be spawned with.
|
---@class _WindowRule Attributes the window will be spawned with.
|
||||||
---@field output OutputName? The output this window will be spawned on. TODO:
|
---@field output OutputName? The output this window will be spawned on. TODO:
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
---@class WindowRule Attributes the window will be spawned with.
|
---@class WindowRule Attributes the window will be spawned with.
|
||||||
---@field output (Output|OutputName)? The output this window will be spawned on. TODO:
|
---@field output (Output|OutputName)? The output this window will be spawned on. TODO:
|
||||||
---@field tags (Tag|TagTable|TagTableNamed|string)[]? The tags this window will be spawned with.
|
---@field tags TagConstructor[]? The tags this window will be spawned with.
|
||||||
---@field floating_or_tiled ("Floating"|"Tiled")? Whether or not this window will be spawned floating or tiled.
|
---@field floating_or_tiled ("Floating"|"Tiled")? Whether or not this window will be spawned floating or tiled.
|
||||||
---@field fullscreen_or_maximized FullscreenOrMaximized? Whether or not this window will be spawned fullscreen, maximized, or forced to neither.
|
---@field fullscreen_or_maximized FullscreenOrMaximized? Whether or not this window will be spawned fullscreen, maximized, or forced to neither.
|
||||||
---@field size { [1]: integer, [2]: integer }? The size the window will spawn with, with [1] being width and [2] being height. This must be a strictly positive integer; putting 0 will crash the compositor.
|
---@field size { [1]: integer, [2]: integer }? The size the window will spawn with, with [1] being width and [2] being height. This must be a strictly positive integer; putting 0 will crash the compositor.
|
||||||
|
|
|
@ -10,39 +10,123 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
pub struct WindowRuleCondition {
|
||||||
pub enum WindowRuleCondition {
|
|
||||||
/// This condition is met when any of the conditions provided is met.
|
/// This condition is met when any of the conditions provided is met.
|
||||||
CondAny(Vec<WindowRuleCondition>),
|
#[serde(default)]
|
||||||
|
cond_any: Option<Vec<WindowRuleCondition>>,
|
||||||
/// This condition is met when all of the conditions provided are met.
|
/// This condition is met when all of the conditions provided are met.
|
||||||
CondAll(Vec<WindowRuleCondition>),
|
#[serde(default)]
|
||||||
|
cond_all: Option<Vec<WindowRuleCondition>>,
|
||||||
/// This condition is met when the class matches.
|
/// This condition is met when the class matches.
|
||||||
Class(String),
|
#[serde(default)]
|
||||||
|
class: Option<Vec<String>>,
|
||||||
/// This condition is met when the title matches.
|
/// This condition is met when the title matches.
|
||||||
Title(String),
|
#[serde(default)]
|
||||||
|
title: Option<Vec<String>>,
|
||||||
/// This condition is met when the tag matches.
|
/// This condition is met when the tag matches.
|
||||||
Tag(TagId),
|
#[serde(default)]
|
||||||
|
tag: Option<Vec<TagId>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum AllOrAny {
|
||||||
|
All,
|
||||||
|
Any,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowRuleCondition {
|
impl WindowRuleCondition {
|
||||||
/// RefCell Safety: This method uses RefCells on `window`.
|
/// RefCell Safety: This method uses RefCells on `window`.
|
||||||
pub fn is_met(&self, state: &State, window: &WindowElement) -> bool {
|
pub fn is_met(&self, state: &State, window: &WindowElement) -> bool {
|
||||||
match self {
|
Self::is_met_inner(self, state, window, AllOrAny::All)
|
||||||
WindowRuleCondition::CondAny(conds) => {
|
}
|
||||||
conds.iter().any(|cond| Self::is_met(cond, state, window))
|
|
||||||
}
|
fn is_met_inner(&self, state: &State, window: &WindowElement, all_or_any: AllOrAny) -> bool {
|
||||||
WindowRuleCondition::CondAll(conds) => {
|
tracing::debug!("{self:#?}");
|
||||||
conds.iter().all(|cond| Self::is_met(cond, state, window))
|
|
||||||
}
|
let WindowRuleCondition {
|
||||||
WindowRuleCondition::Class(class) => window.class().as_ref() == Some(class),
|
cond_any,
|
||||||
WindowRuleCondition::Title(title) => window.title().as_ref() == Some(title),
|
cond_all,
|
||||||
WindowRuleCondition::Tag(tag) => {
|
class,
|
||||||
let Some(tag) = tag.tag(state) else {
|
title,
|
||||||
tracing::warn!("WindowRuleCondition no tag");
|
tag,
|
||||||
return false;
|
} = self;
|
||||||
|
|
||||||
|
match all_or_any {
|
||||||
|
AllOrAny::All => {
|
||||||
|
let cond_any = if let Some(cond_any) = cond_any {
|
||||||
|
cond_any
|
||||||
|
.iter()
|
||||||
|
.any(|cond| Self::is_met_inner(cond, state, window, AllOrAny::Any))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let cond_all = if let Some(cond_all) = cond_all {
|
||||||
|
cond_all
|
||||||
|
.iter()
|
||||||
|
.all(|cond| Self::is_met_inner(cond, state, window, AllOrAny::All))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let classes = if let Some(classes) = class {
|
||||||
|
classes
|
||||||
|
.iter()
|
||||||
|
.all(|class| window.class().as_ref() == Some(class))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let titles = if let Some(titles) = title {
|
||||||
|
titles
|
||||||
|
.iter()
|
||||||
|
.all(|title| window.title().as_ref() == Some(title))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let tags = if let Some(tag_ids) = tag {
|
||||||
|
let mut tags = tag_ids.iter().filter_map(|tag_id| tag_id.tag(state));
|
||||||
|
tags.all(|tag| window.with_state(|state| state.tags.contains(&tag)))
|
||||||
|
} else {
|
||||||
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
window.with_state(|state| state.tags.contains(&tag))
|
tracing::debug!("{cond_all} {cond_any} {classes} {titles} {tags}");
|
||||||
|
cond_all && cond_any && classes && titles && tags
|
||||||
|
}
|
||||||
|
AllOrAny::Any => {
|
||||||
|
let cond_any = if let Some(cond_any) = cond_any {
|
||||||
|
cond_any
|
||||||
|
.iter()
|
||||||
|
.any(|cond| Self::is_met_inner(cond, state, window, AllOrAny::Any))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let cond_all = if let Some(cond_all) = cond_all {
|
||||||
|
cond_all
|
||||||
|
.iter()
|
||||||
|
.all(|cond| Self::is_met_inner(cond, state, window, AllOrAny::All))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let classes = if let Some(classes) = class {
|
||||||
|
classes
|
||||||
|
.iter()
|
||||||
|
.any(|class| window.class().as_ref() == Some(class))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let titles = if let Some(titles) = title {
|
||||||
|
titles
|
||||||
|
.iter()
|
||||||
|
.any(|title| window.title().as_ref() == Some(title))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let tags = if let Some(tag_ids) = tag {
|
||||||
|
let mut tags = tag_ids.iter().filter_map(|tag_id| tag_id.tag(state));
|
||||||
|
tags.any(|tag| window.with_state(|state| state.tags.contains(&tag)))
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
cond_all || cond_any || classes || titles || tags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,12 +224,6 @@ impl XwmHandler for CalloopData {
|
||||||
.cloned();
|
.cloned();
|
||||||
if let Some(win) = win {
|
if let Some(win) = win {
|
||||||
self.state.space.unmap_elem(&win);
|
self.state.space.unmap_elem(&win);
|
||||||
// self.state.windows.retain(|elem| &win != elem);
|
|
||||||
// if win.with_state(|state| state.floating.is_tiled()) {
|
|
||||||
// if let Some(output) = win.output(&self.state) {
|
|
||||||
// self.state.re_layout(&output);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
if !window.is_override_redirect() {
|
if !window.is_override_redirect() {
|
||||||
tracing::debug!("set mapped to false");
|
tracing::debug!("set mapped to false");
|
||||||
|
@ -290,7 +284,6 @@ impl XwmHandler for CalloopData {
|
||||||
geometry: Rectangle<i32, Logical>,
|
geometry: Rectangle<i32, Logical>,
|
||||||
_above: Option<smithay::reexports::x11rb::protocol::xproto::Window>,
|
_above: Option<smithay::reexports::x11rb::protocol::xproto::Window>,
|
||||||
) {
|
) {
|
||||||
// tracing::debug!("x11 configure_notify");
|
|
||||||
let Some(win) = self
|
let Some(win) = self
|
||||||
.state
|
.state
|
||||||
.space
|
.space
|
||||||
|
|
Loading…
Add table
Reference in a new issue