From 5cd44b1ee83925599ce9b3c932a075f1fed857ec Mon Sep 17 00:00:00 2001 From: Yessiest Date: Sat, 4 Mar 2023 15:25:35 +0400 Subject: [PATCH] Mergeable menus, finally --- desktop.conf | 2 +- libs/context_menu.lua | 43 +++++++++--------- widgets/clientmenu.lua | 78 +++++++++++++++++---------------- widgets/clientmenu/controls.lua | 14 +++--- widgets/rootmenu.lua | 43 ++++++++++-------- widgets/rootmenu/controls.lua | 9 +--- widgets/xdgmenu.lua | 49 ++------------------- 7 files changed, 97 insertions(+), 141 deletions(-) diff --git a/desktop.conf b/desktop.conf index 97ab178..88929ea 100644 --- a/desktop.conf +++ b/desktop.conf @@ -3,7 +3,7 @@ terminal = "$HOME/.local/bin/st" browser = "prime-run librewolf" modkey = "Mod4" -theme = "serenity" +theme = "unity" shell = "zsh" # Keybindings diff --git a/libs/context_menu.lua b/libs/context_menu.lua index 3a77106..46e1b7d 100644 --- a/libs/context_menu.lua +++ b/libs/context_menu.lua @@ -37,7 +37,7 @@ end return function(args) -- A way to communicate that all widgets in menu got closed args.on_close = args.on_close or function() end - local style = awmtk2.create_style("menu", + local style = awmtk2.create_style("menu", awmtk2.generic.menu, args.style) local templates = awmtk2.create_template_lib("menu", awmtk2.templates, args.templates) local t = awmtk2.build_templates(templates,style) @@ -53,13 +53,14 @@ return function(args) local onpress = function(widget) style.button.onrelease(widget) widget:emit_signal("cascade::kill") - if root_layer.focused then - root_layer.focused:emit_signal("cascade::kill") + if root_layer._private.focused then + root_layer._private.focused:emit_signal("cascade::kill") + root_layer:emit_signal("cascade::kill") end end if type(element[2]) == "string" then new_element:connect_signal("button::press",style.button.onpress) - new_element:connect_signal("button::press",function(widget) + new_element:connect_signal("button::press",function() awful.spawn(element[2]) end) new_element:connect_signal("button::release",onpress) @@ -68,12 +69,12 @@ return function(args) new_element:connect_signal("button::press",element[2]) new_element:connect_signal("button::release",onpress) elseif type(element[2]) == "table" then - local layout = { + local layout = wibox.widget({ spacing = style.base.spacing, layout = wibox.layout.fixed.vertical - } - for k,v in pairs(element[2]) do - table.insert(layout,menu_builder(v,layout,root_layer)) + }) + for _,v in pairs(element[2]) do + layout:add(menu_builder(v,layout,root_layer)) end local next_layer = awful.popup(t.popup(layout,{ visible = false, @@ -82,29 +83,31 @@ return function(args) preferred_anchors = {"front","back"}, })) local function open_layer(widget) - if layer.focused == widget and - next_layer.visible then - return + if layer._private.focused == widget and + next_layer.visible then + return end - if layer.focused then - layer.focused:emit_signal("cascade::close") + if layer._private.focused then + layer._private.focused:emit_signal("cascade::close") end - layer.focused = widget + layer._private.focused = widget position_popup(next_layer, new_element, style) end local onclose = function() style.button.onrelease(new_element) - if layout.focused then - layout.focused:emit_signal("cascade::close") + if layout._private.focused then + layout._private.focused:emit_signal("cascade::close") end next_layer.visible = false end new_element:connect_signal("cascade::close",onclose) new_element:connect_signal("cascade::kill",onclose) + root_layer:connect_signal("cascade::kill",onclose) + root_layer:connect_signal("cascade::close",onclose) -- that sweet "just move the mouse 4head" navigation if style.base.menu_slide then new_element:connect_signal("mouse::enter",open_layer) - new_element:connect_signal("mouse::enter",style.button.onpress) + new_element:connect_signal("mouse::enter",style.button.onpress) else new_element:connect_signal("button::press",style.button.onpress) new_element:connect_signal("button::press",open_layer) @@ -112,13 +115,13 @@ return function(args) end return new_element end - local root_layer = { + local root_layer = args.parent or wibox.widget { layout = wibox.layout.fixed.vertical, id = "menu_root", spacing = style.base.spacing } - for k,v in pairs(args.items) do - table.insert(root_layer,menu_builder(v,root_layer,root_layer)) + for _,v in pairs(args.items) do + root_layer:add(menu_builder(v,root_layer,root_layer)) end return root_layer end diff --git a/widgets/clientmenu.lua b/widgets/clientmenu.lua index 2e53cd7..173baee 100644 --- a/widgets/clientmenu.lua +++ b/widgets/clientmenu.lua @@ -11,54 +11,56 @@ 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 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 t = awmtk2.build_templates(templates,style,false) -- Create a global context menu for clients first -- This saves us memory on not creating separate menus for every client if not context_menu then -local config_file = io.open(root_path.."/themes/"..global.theme..'/config/client_menu.json',"r") -local config -if config_file then - config = config_file:read("*a") - config_file:close() -else - config = [[{"list":[{"widget":"widgets.clientcontrols"}]}]] -end -context_menu = awful.popup(t.popup(builder( - config, - { - style = style.base, - } -))) -context_menu:connect_signal("button::press",function(self,x,y,b) - if b == 3 then - context_menu.visible = false + -- load client menu config + local config_file = io.open(root_path.."/themes/"..global.theme..'/config/client_menu.json',"r") + local config + if config_file then + config = config_file:read("*a") + config_file:close() + else + config = [[{"list":[{"widget":"widgets.clientcontrols"}]}]] end -end) -context_menu:connect_signal("property::visible",function(self,x,y,b) - if not context_menu.visible then - local children = context_menu.widget:get_children_by_id("menu_root") - for k,v in pairs(children) do - for k2,v2 in pairs(v.children) do - v2:emit_signal("cascade::close") - end - end - end -end) -client.connect_signal("focus",function() - context_menu.visible = false -end) -for _,layout in pairs(context_menu.widget:get_children_by_id("menu_root")) do - for _,button in pairs(layout.children) do - button:connect_signal("cascade::kill",function() + -- 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 + context_menu:connect_signal("button::press",function(_,_,_,b) + if b == 3 then context_menu.visible = false - end) - end -end + end + end) + -- Close all cascading menus if our menu becomes invisible + context_menu:connect_signal("property::visible",function() + if not context_menu.visible then + prime_menu:emit_signal("cascade::close") + 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) end root.keys(gears.table.join( root.keys(), diff --git a/widgets/clientmenu/controls.lua b/widgets/clientmenu/controls.lua index 9789dfa..fca326b 100644 --- a/widgets/clientmenu/controls.lua +++ b/widgets/clientmenu/controls.lua @@ -6,23 +6,18 @@ -- -- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see . -- Basic client control menu -local awmtk2 = require("awmtk2") local awful = require("awful") local menugen = require("context_menu") return function(args) - local style = awmtk2.create_style("client_controls", - awmtk2.generic.menu,args.style,args.vertical) - local templates = awmtk2.create_template_lib("client_controls",awmtk2.templates,args.templates) - local t = awmtk2.build_templates(templates,style,args.vertical) local move_to_tag = {} local add_to_tag = {} awful.screen.connect_for_each_screen(function(s) table.insert(move_to_tag,{ "Screen "..s.index, - (function() + (function() local t = {} - for k,v in pairs(s.tags) do + for _,v in pairs(s.tags) do table.insert(t,{v.name,function() if client.focus then client.focus:tags({v}) @@ -34,9 +29,9 @@ return function(args) }) table.insert(add_to_tag,{ "Screen "..s.index, - (function() + (function() local t = {} - for k,v in pairs(s.tags) do + for _,v in pairs(s.tags) do table.insert(t,{v.name,function() if client.focus then local tags = client.focus:tags() @@ -65,6 +60,7 @@ return function(args) { "Move to tag", move_to_tag }, { "Switch on tag", add_to_tag } }, + parent = args.menu_parent }) return widget end diff --git a/widgets/rootmenu.lua b/widgets/rootmenu.lua index 033ff13..0e28a3e 100644 --- a/widgets/rootmenu.lua +++ b/widgets/rootmenu.lua @@ -10,7 +10,7 @@ local awmtk2 = require("awmtk2") local wibox = require("wibox") local gears = require("gears") local awful = require("awful") -local beautiful = require("beautiful") +local menugen = require("context_menu") local builder = require("builder") return function(args) @@ -18,49 +18,54 @@ return function(args) awmtk2.generic.composite_widget,{}) local templates = awmtk2.create_template_lib("root_menu",awmtk2.templates,{}) local t = awmtk2.build_templates(templates,style,args.vertical) + -- Layout configuration local config_file = io.open(root_path.."/themes/"..global.theme.."/config/root_menu.json","r") local config if config_file then config = config_file:read("*a") config_file:close() - else + else config = [[{"list": [{"widget": "widgets.rootcontrols"}],"vertical": true}]] end - -- TODO: Refactor this whole mess + -- 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 })) - root_menu:connect_signal("button::press",function(self,x,y,b) + -- Close popup on right click + root_menu:connect_signal("button::press",function(_,_,_,b) if b == 3 then root_menu.visible = false end end) + -- Build the menu based on the json config root_menu.widget = wibox.widget(t.popup(builder( config, { style = style.base, screen = mouse.screen, passthrough = { - parent = root_menu + -- This is the menu to which other menus should attach (available as args.menu_parent) + menu_parent = prime_menu, } - } + } )).widget) - for _,layout in pairs(root_menu.widget:get_children_by_id("menu_root")) do - for _,button in pairs(layout.children) do - button:connect_signal("cascade::kill",function() - root_menu.visible = false - end) - end - end + -- Close the menu if prime_menu received a cascade::kill signal + prime_menu:connect_signal("cascade::kill",function() + root_menu.visible = false + end) + -- Close the prime_menu if our menu becomes invisible root_menu:connect_signal("property::visible",function() - local roots = root_menu.widget:get_children_by_id("menu_root") - for k,v in pairs(roots) do - for _,w in ipairs(v.children) do - w:emit_signal("cascade::close") - end + if not root_menu.visible then + prime_menu:emit_signal_recursive("cascade::close") end end) + -- Make the root_menu pop up on the desktop on right click local buttons = root.buttons() root.buttons(gears.table.join(buttons, awful.button({}, 3, function() @@ -74,4 +79,4 @@ return function(args) end) )) return root_menu -end +end diff --git a/widgets/rootmenu/controls.lua b/widgets/rootmenu/controls.lua index 47f0186..0124e72 100644 --- a/widgets/rootmenu/controls.lua +++ b/widgets/rootmenu/controls.lua @@ -6,18 +6,10 @@ -- -- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see . -- Simple global context menu controls -local awmtk2 = require("awmtk2") -local wibox = require("wibox") -local gears = require("gears") -local awful = require("awful") local beautiful = require("beautiful") local menugen = require("context_menu") return function(args) - local style = awmtk2.create_style("root_menu", - awmtk2.generic.menu,args.style,args.vertical) - local templates = awmtk2.create_template_lib("root_menu",awmtk2.templates,args.templates) - local t = awmtk2.build_templates(templates,style,args.vertical) local widget = menugen({ items = { {"Awesome", { @@ -28,6 +20,7 @@ return function(args) }, beautiful.awesome_icon}, {"open terminal", global.terminal}, }, + parent = args.menu_parent }) return widget end diff --git a/widgets/xdgmenu.lua b/widgets/xdgmenu.lua index f348b96..2ff6fa8 100644 --- a/widgets/xdgmenu.lua +++ b/widgets/xdgmenu.lua @@ -43,9 +43,7 @@ return function(args) end return false end - awesome.connect_signal("xdg::all_finished",function() - if not args.parent then return end local items = {} for k,v in pairs(xdg.categories) do local noprocess = false @@ -71,51 +69,10 @@ 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() - local menu = wibox.widget(menugen({ + menugen({ items = items, - })) - local menu_root = menu:get_children_by_id("menu_root")[1] - for _,v in pairs(menu_root.children) do - v:connect_signal("cascade::kill",function() - args.parent.visible = false - end) - args.parent:connect_signal("property::visible",function() - if not args.parent.visible then - v:emit_signal("cascade::close") - end - end) - menu:connect_signal("widget::redraw_needed",function() - if not menu.visible then - v:emit_signal("cascade::close") - end - end) - end - local appswitch = wibox.widget(t.button(t.textbox({ - markup = "Applications", - id = "apptext" - }),{ - forced_height = style.button.forced_height, - forced_width = style.button.forced_width - })) - appswitch:connect_signal("button::press",function(self) - menu.visible = (not menu.visible) - if not menu.visible then - xdg_menu_root.spacing = 0 - else - xdg_menu_root.spacing = style.base.spacing - end - local textbox = appswitch:get_children_by_id("apptext")[1] - if menu.visible then - style.button.onpress(self) - textbox:set_markup("Applications") - else - style.button.onrelease(self) - textbox:set_markup("Applications") - end - end) - menu.visible = false - xdg_menu_root:add(appswitch) - xdg_menu_root:add(menu) + parent = args.menu_parent + }) end) return widget end