FINALLY, ASYNCHRONOUS MULTIMENUS

This commit is contained in:
Yessiest 2023-03-05 16:53:37 +04:00
parent 39ca509391
commit c6ca21f73a
15 changed files with 155 additions and 62 deletions

View File

@ -3,7 +3,7 @@
terminal = "$HOME/.local/bin/st"
browser = "prime-run librewolf"
modkey = "Mod4"
theme = "unity"
theme = "serenity"
shell = "zsh"
# Keybindings

View File

@ -10,6 +10,7 @@ local json = require("dkjson")
local gears = require("gears")
local wibox = require("wibox")
local awful = require("awful")
local menu = require("context_menu")
local builtins = {
h_spacer = function(o)
@ -126,10 +127,20 @@ return function(description,opts)
return builtins[struct.builtin](gears.table.join({
client = (struct.client and c)
},struct.options or {}))
elseif struct.multimenu then
local multimenu = menu({items={}})
if not opts.passthrough then
opts.passthrough = {}
end
opts.passthrough.menu_parent = multimenu
for _,v in pairs(struct.multimenu) do
inner_builder(v,struct.vertical)
end
return multimenu
end
-- If this gets interpreted it's safe to say none of the constructions
-- above got matched.
print("Object where the error occured: ")
print("Object dump: ")
gears.debug.dump(struct)
error("Builder error: invalid object description")
end

29
libs/digger.lua Normal file
View File

@ -0,0 +1,29 @@
-- Since it would seem that get_widget_by_id is still an open issue (https://github.com/awesomeWM/awesome/issues/2945, https://github.com/awesomeWM/awesome/issues/2181), this abomination is the nuclear solution to finding **ALL** widgets that don't get indexed because they are separated by an already instantiated widget. If you use this, please, use it carefully. Don't call it more than you really need, cache the results if you have to.
-- Hypothetically, if there occurs such a thing as widget cycle, this will get stuck. Also it's very expensive.
return function(widget, id)
local results = {}
local checked = {}
local function walker(widget, id)
if widget.children then
for _,v in pairs(widget:get_children()) do
if (v.id == id) and (not checked[v]) then
table.insert(results, v)
checked[v] = true
end
if (v._private.by_id and v._private.by_id[id]) then
for _,v2 in pairs(v._private.by_id[id]) do
if not checked[v2] then
table.insert(results,v2)
checked[v2] = true
end
end
end
walker(v, id)
end
end
end
walker(widget, id)
return results
end

View File

@ -12,14 +12,16 @@
{"widget": "widgets.base.tagswitcher",
"screen":true
},
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
{"multimenu": [
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
}
}
},
]},
{"widget": "widgets.rootmenu.buttons"}
],
"vertical": true

View File

@ -12,14 +12,16 @@
{"widget": "widgets.base.tagswitcher",
"screen":true
},
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
{"multimenu":[
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
"exclude_category": [
"Other"
]
}
}
},
]},
{"widget": "widgets.rootmenu.buttons"}
],
"vertical": true

View File

@ -11,14 +11,16 @@
{"widget": "widgets.base.tagswitcher",
"screen":true
},
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
{"multimenu": [
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
}
}
},
]},
{"widget": "widgets.rootmenu.buttons"}
],
"vertical": true

View File

@ -11,14 +11,16 @@
{"widget": "widgets.base.tagswitcher",
"screen":true
},
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
{"multimenu":[
{"widget": "widgets.rootmenu.controls"},
{"widget": "widgets.xdgmenu",
"options": {
"exclude_category": [
"Other"
]
}
}
},
]},
{"widget": "widgets.rootmenu.buttons"}
],
"vertical": true

View File

@ -11,8 +11,8 @@ local wibox = require("wibox")
local awful = require("awful")
local gears = require("gears")
local builder = require("builder")
local menugen = require("context_menu")
local ask = require("asckey")
local digger = require("digger")
local style = awmtk2.create_style("client_menu",
awmtk2.generic.composite_widget,{})
local templates = awmtk2.create_template_lib("client_menu",awmtk2.templates,{})
@ -23,22 +23,17 @@ local t = awmtk2.build_templates(templates,style,false)
if not context_menu then
-- load client menu config
local config_file = io.open(root_path.."/themes/"..global.theme..'/config/client_menu.json',"r")
local config
local config
if config_file then
config = config_file:read("*a")
config_file:close()
else
config = [[{"list":[{"widget":"widgets.clientcontrols"}]}]]
end
-- NOTE: Please attach your menus to the prime_menu. It should be available as args.menu_parent via passthrough
local prime_menu = menugen({items={}})
context_menu = awful.popup(t.popup(builder(
config,
{
style = style.base,
passthrough = {
menu_parent = prime_menu
}
}
)))
-- Close context menu on right click
@ -47,20 +42,46 @@ if not context_menu then
context_menu.visible = false
end
end)
-- Generate a list of all existing menu_root objects
local menus = digger(context_menu.widget,"menu_root")
local context_menu_size = context_menu.width * context_menu.height
context_menu.widget:connect_signal("widget::size_changed",function()
local already_managed = {}
for _,v in pairs(menus) do
already_managed[v] = true
end
menus = digger(context_menu.widget,"menu_root")
for _,v in pairs(menus) do
if not already_managed[v] then
v:connect_signal("cascade::kill",function()
context_menu.visible = false
end)
end
end
end)
-- Close all cascading menus if our menu becomes invisible
context_menu:connect_signal("property::visible",function()
local current_context_menu_size = context_menu.height * context_menu.width
if current_context_menu_size ~= context_menu_size then
context_menu:emit_signal("widget::size_changed")
context_menu_size = current_context_menu_size
end
if not context_menu.visible then
prime_menu:emit_signal("cascade::close")
for _,v in pairs(menus) do
v:emit_signal_recursive("cascade::kill")
end
end
end)
-- If client became unfocused, close menu
client.connect_signal("focus",function()
context_menu.visible = false
end)
-- Close our context menu when any of the attached menus signal cascade::kill
prime_menu:connect_signal("cascade::kill",function()
context_menu.visible = false
end)
-- If any of the menus emits cascade::kill, close the menu
for _,v in pairs(menus) do
v:connect_signal("cascade::kill",function()
context_menu.visible = false
end)
end
end
root.keys(gears.table.join(
root.keys(),

View File

@ -20,7 +20,7 @@ if _VERSION:match("5.1") then
result = (test_pactl == 0)
end
if not result then
return function() end
return
end
local function get_icon(percent)

View File

@ -53,6 +53,11 @@ return function(args)
})
-- create popup
local popup = awful.popup(t.popup(layout))
popup:connect_signal("button::press",function(_,_,_,b)
if b == 3 then
popup.visible = false
end
end)
local battery_widget
do -- create battery widget
local style = awmtk2.create_style("battery",

View File

@ -9,7 +9,6 @@
local awful = require("awful")
local pager = require("pager")
local beautiful = require("beautiful")
local gears = require("gears")
local wibox = require("wibox")
local awmtk2 = require("awmtk2")
local naughty = require("naughty")
@ -49,6 +48,11 @@ return function(args)
},{
visible = false
}))
popup:connect_signal("button::press",function(_,_,_,b)
if b == 3 then
popup.visible = false
end
end)
naughty.config.notify_callback = function(update_args)
count = count + 1
local w = wibox.widget(t.button(t.article({

View File

@ -24,7 +24,7 @@ if _VERSION:match("5.1") then
amixer_found = (test_amixer == 0)
end
if (not (amixer_found or pactl_found)) then
return function() end
return
end
local try_launch = "pavucontrol"

View File

@ -33,8 +33,6 @@ local xdg_search = function(name,rlimit,sorting_method)
table.sort(keys,function(a,b) return a > b end)
local count = 0
local exit = false
gears.debug.dump(keys)
gears.debug.dump(filter)
for k = 1,rlimit do
local i = keys[k]
if not filter[i] then
@ -52,7 +50,6 @@ local xdg_search = function(name,rlimit,sorting_method)
break
end
end
gears.debug.dump(ranked_results)
elseif sorting_method == "recent" then
local most_recent = 0
for k,v in pairs(xdg.apps) do

View File

@ -10,8 +10,8 @@ local awmtk2 = require("awmtk2")
local wibox = require("wibox")
local gears = require("gears")
local awful = require("awful")
local menugen = require("context_menu")
local builder = require("builder")
local digger = require("digger")
return function(args)
local style = awmtk2.create_style("root_menu",
@ -27,14 +27,9 @@ return function(args)
else
config = [[{"list": [{"widget": "widgets.rootcontrols"}],"vertical": true}]]
end
-- NOTE: **PLEASE** attach your other menus to this thing, ok? IF YOU DON'T, THE MENUS WILL WORK LIKE SHIT
-- NOTE 2: THERE IS NO OTHER "PROPER" FIX FOR THIS, JUST LOOK AT THE PREVIOUS SYSTEM (SPOILER: it was ABSOLUTE GARBAGE)
-- Primary menu to which other menus get attached
local prime_menu = menugen({items = {}})
-- Not yet loaded menu popup (TODO: maybe make it more obvious that it's a global?)
root_menu = awful.popup(t.popup({
markup = "brainhurt the game",
prime_menu,
widget = wibox.widget.textbox
}))
-- Close popup on right click
@ -49,22 +44,44 @@ return function(args)
{
style = style.base,
screen = mouse.screen,
passthrough = {
-- This is the menu to which other menus should attach (available as args.menu_parent)
menu_parent = prime_menu,
}
}
)).widget)
-- Close the menu if prime_menu received a cascade::kill signal
prime_menu:connect_signal("cascade::kill",function()
root_menu.visible = false
-- Generate a list of all existing menu_root objects
local menus = digger(root_menu.widget,"menu_root")
local root_menu_size = root_menu.width * root_menu.height
root_menu:connect_signal("widget::size_changed",function()
local already_managed = {}
for _,v in pairs(menus) do
already_managed[v] = true
end
menus = digger(root_menu.widget,"menu_root")
for _,v in pairs(menus) do
if not already_managed[v] then
v:connect_signal("cascade::kill",function()
root_menu.visible = false
end)
end
end
end)
-- Close the prime_menu if our menu becomes invisible
root_menu:connect_signal("property::visible",function()
local current_root_menu_size = root_menu.width * root_menu.height
if current_root_menu_size ~= root_menu_size then
root_menu:emit_signal("widget::size_changed")
root_menu_size = current_root_menu_size
end
if not root_menu.visible then
prime_menu:emit_signal_recursive("cascade::close")
for _,v in pairs(menus) do
v:emit_signal("cascade::kill")
end
end
end)
-- If any of the menus emits cascade::kill, close the menu
for _,v in pairs(menus) do
v:connect_signal("cascade::kill",function()
root_menu.visible = false
end)
end
-- Make the root_menu pop up on the desktop on right click
local buttons = root.buttons()
root.buttons(gears.table.join(buttons,

View File

@ -69,10 +69,11 @@ return function(args)
-- uhhh there's a lot of things about async, some of which i can't explain
local xdg_menu_root = widget:get_children_by_id("xdg_menu_root")[1]
xdg_menu_root:reset()
menugen({
local menu = menugen({
items = items,
parent = args.menu_parent
})
xdg_menu_root:add(menu)
end)
return widget
end