Browse Source

FINALLY, ASYNCHRONOUS MULTIMENUS

master
Yessiest 1 year ago
parent
commit
c6ca21f73a
  1. 2
      desktop.conf
  2. 13
      libs/builder.lua
  3. 29
      libs/digger.lua
  4. 16
      themes/reno98/config/root_menu.json
  5. 14
      themes/serenity/config/root_menu.json
  6. 16
      themes/unity/config/root_menu.json
  7. 16
      themes/unity_mate/config/root_menu.json
  8. 45
      widgets/clientmenu.lua
  9. 2
      widgets/clientmenu/volume.lua
  10. 5
      widgets/desktop/battery.lua
  11. 6
      widgets/desktop/notifications.lua
  12. 2
      widgets/desktop/volume.lua
  13. 3
      widgets/dismal.lua
  14. 45
      widgets/rootmenu.lua
  15. 3
      widgets/xdgmenu.lua

2
desktop.conf

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

13
libs/builder.lua

@ -10,6 +10,7 @@ local json = require("dkjson")
local gears = require("gears") local gears = require("gears")
local wibox = require("wibox") local wibox = require("wibox")
local awful = require("awful") local awful = require("awful")
local menu = require("context_menu")
local builtins = { local builtins = {
h_spacer = function(o) h_spacer = function(o)
@ -126,10 +127,20 @@ return function(description,opts)
return builtins[struct.builtin](gears.table.join({ return builtins[struct.builtin](gears.table.join({
client = (struct.client and c) client = (struct.client and c)
},struct.options or {})) },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 end
-- If this gets interpreted it's safe to say none of the constructions -- If this gets interpreted it's safe to say none of the constructions
-- above got matched. -- above got matched.
print("Object where the error occured: ")
print("Object dump: ")
gears.debug.dump(struct) gears.debug.dump(struct)
error("Builder error: invalid object description") error("Builder error: invalid object description")
end end

29
libs/digger.lua

@ -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

16
themes/reno98/config/root_menu.json

@ -12,14 +12,16 @@
{"widget": "widgets.base.tagswitcher", {"widget": "widgets.base.tagswitcher",
"screen":true "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"} {"widget": "widgets.rootmenu.buttons"}
], ],
"vertical": true "vertical": true

14
themes/serenity/config/root_menu.json

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

16
themes/unity/config/root_menu.json

@ -11,14 +11,16 @@
{"widget": "widgets.base.tagswitcher", {"widget": "widgets.base.tagswitcher",
"screen":true "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"} {"widget": "widgets.rootmenu.buttons"}
], ],
"vertical": true "vertical": true

16
themes/unity_mate/config/root_menu.json

@ -11,14 +11,16 @@
{"widget": "widgets.base.tagswitcher", {"widget": "widgets.base.tagswitcher",
"screen":true "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"} {"widget": "widgets.rootmenu.buttons"}
], ],
"vertical": true "vertical": true

45
widgets/clientmenu.lua

@ -11,8 +11,8 @@ local wibox = require("wibox")
local awful = require("awful") local awful = require("awful")
local gears = require("gears") local gears = require("gears")
local builder = require("builder") local builder = require("builder")
local menugen = require("context_menu")
local ask = require("asckey") local ask = require("asckey")
local digger = require("digger")
local style = awmtk2.create_style("client_menu", local style = awmtk2.create_style("client_menu",
awmtk2.generic.composite_widget,{}) awmtk2.generic.composite_widget,{})
local templates = awmtk2.create_template_lib("client_menu",awmtk2.templates,{}) 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 if not context_menu then
-- load client menu config -- load client menu config
local config_file = io.open(root_path.."/themes/"..global.theme..'/config/client_menu.json',"r") local config_file = io.open(root_path.."/themes/"..global.theme..'/config/client_menu.json',"r")
local config
local config
if config_file then if config_file then
config = config_file:read("*a") config = config_file:read("*a")
config_file:close() config_file:close()
else else
config = [[{"list":[{"widget":"widgets.clientcontrols"}]}]] config = [[{"list":[{"widget":"widgets.clientcontrols"}]}]]
end 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( context_menu = awful.popup(t.popup(builder(
config, config,
{ {
style = style.base, style = style.base,
passthrough = {
menu_parent = prime_menu
}
} }
))) )))
-- Close context menu on right click -- Close context menu on right click
@ -47,20 +42,46 @@ if not context_menu then
context_menu.visible = false context_menu.visible = false
end end
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 -- Close all cascading menus if our menu becomes invisible
context_menu:connect_signal("property::visible",function() 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 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
end) end)
-- If client became unfocused, close menu -- If client became unfocused, close menu
client.connect_signal("focus",function() client.connect_signal("focus",function()
context_menu.visible = false context_menu.visible = false
end) 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 end
root.keys(gears.table.join( root.keys(gears.table.join(
root.keys(), root.keys(),

2
widgets/clientmenu/volume.lua

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

5
widgets/desktop/battery.lua

@ -53,6 +53,11 @@ return function(args)
}) })
-- create popup -- create popup
local popup = awful.popup(t.popup(layout)) 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 local battery_widget
do -- create battery widget do -- create battery widget
local style = awmtk2.create_style("battery", local style = awmtk2.create_style("battery",

6
widgets/desktop/notifications.lua

@ -9,7 +9,6 @@
local awful = require("awful") local awful = require("awful")
local pager = require("pager") local pager = require("pager")
local beautiful = require("beautiful") local beautiful = require("beautiful")
local gears = require("gears")
local wibox = require("wibox") local wibox = require("wibox")
local awmtk2 = require("awmtk2") local awmtk2 = require("awmtk2")
local naughty = require("naughty") local naughty = require("naughty")
@ -49,6 +48,11 @@ return function(args)
},{ },{
visible = false 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) naughty.config.notify_callback = function(update_args)
count = count + 1 count = count + 1
local w = wibox.widget(t.button(t.article({ local w = wibox.widget(t.button(t.article({

2
widgets/desktop/volume.lua

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

3
widgets/dismal.lua

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

45
widgets/rootmenu.lua

@ -10,8 +10,8 @@ local awmtk2 = require("awmtk2")
local wibox = require("wibox") local wibox = require("wibox")
local gears = require("gears") local gears = require("gears")
local awful = require("awful") local awful = require("awful")
local menugen = require("context_menu")
local builder = require("builder") local builder = require("builder")
local digger = require("digger")
return function(args) return function(args)
local style = awmtk2.create_style("root_menu", local style = awmtk2.create_style("root_menu",
@ -27,14 +27,9 @@ return function(args)
else else
config = [[{"list": [{"widget": "widgets.rootcontrols"}],"vertical": true}]] config = [[{"list": [{"widget": "widgets.rootcontrols"}],"vertical": true}]]
end 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?) -- Not yet loaded menu popup (TODO: maybe make it more obvious that it's a global?)
root_menu = awful.popup(t.popup({ root_menu = awful.popup(t.popup({
markup = "brainhurt the game", markup = "brainhurt the game",
prime_menu,
widget = wibox.widget.textbox widget = wibox.widget.textbox
})) }))
-- Close popup on right click -- Close popup on right click
@ -49,22 +44,44 @@ return function(args)
{ {
style = style.base, style = style.base,
screen = mouse.screen, screen = mouse.screen,
passthrough = {
-- This is the menu to which other menus should attach (available as args.menu_parent)
menu_parent = prime_menu,
}
} }
)).widget) )).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) end)
-- Close the prime_menu if our menu becomes invisible -- Close the prime_menu if our menu becomes invisible
root_menu:connect_signal("property::visible",function() 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 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
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 -- Make the root_menu pop up on the desktop on right click
local buttons = root.buttons() local buttons = root.buttons()
root.buttons(gears.table.join(buttons, root.buttons(gears.table.join(buttons,

3
widgets/xdgmenu.lua

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