awful.menu: rewrite
Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
2f3bc619cb
commit
ea40a922ed
|
@ -1,34 +1,53 @@
|
|||
---------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
-- @author Damien Leone <damien.leone@gmail.com>
|
||||
-- @author Julien Danjou <julien@danjou.info>
|
||||
-- @copyright 2008 Damien Leone, Julien Danjou
|
||||
-- @author dodo
|
||||
-- @copyright 2008, 2011 Damien Leone, Julien Danjou, dodo
|
||||
-- @release @AWESOME_VERSION@
|
||||
---------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Grab environment we need
|
||||
local pairs = pairs
|
||||
local table = table
|
||||
local string = string
|
||||
local type = type
|
||||
local setmetatable = setmetatable
|
||||
local tonumber = tonumber
|
||||
local capi =
|
||||
{
|
||||
screen = screen,
|
||||
mouse = mouse,
|
||||
client = client,
|
||||
keygrabber = keygrabber,
|
||||
oocairo = oocairo
|
||||
}
|
||||
local wibox = require("wibox")
|
||||
local button = require("awful.button")
|
||||
local util = require("awful.util")
|
||||
local tags = require("awful.tag")
|
||||
local awbeautiful = require("beautiful")
|
||||
local beautiful = require("beautiful")
|
||||
local object = require("gears.object")
|
||||
local setmetatable = setmetatable
|
||||
local tonumber = tonumber
|
||||
local string = string
|
||||
local ipairs = ipairs
|
||||
local pairs = pairs
|
||||
local pcall = pcall
|
||||
local print = print
|
||||
local table = table
|
||||
local type = type
|
||||
local math = math
|
||||
local capi = {
|
||||
timer = timer,
|
||||
screen = screen,
|
||||
mouse = mouse,
|
||||
client = client,
|
||||
keygrabber = keygrabber,
|
||||
oocairo = oocairo }
|
||||
|
||||
|
||||
--- Creation of menus.
|
||||
module("awful.menu")
|
||||
|
||||
|
||||
local table_update = function (t, set)
|
||||
for k, v in pairs(set) do
|
||||
t[k] = v
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local table_merge = function (t, set)
|
||||
for _, v in ipairs(set) do
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local cur_menu
|
||||
|
||||
--- Key bindings for menu navigation.
|
||||
|
@ -43,121 +62,122 @@ menu_keys = { up = { "Up" },
|
|||
back = { "Left" },
|
||||
close = { "Escape" } }
|
||||
|
||||
local function load_theme(custom)
|
||||
local theme = {}
|
||||
local beautiful
|
||||
|
||||
beautiful = awbeautiful.get()
|
||||
|
||||
theme.fg_focus = custom.fg_focus or beautiful.menu_fg_focus or beautiful.fg_focus
|
||||
theme.bg_focus = custom.bg_focus or beautiful.menu_bg_focus or beautiful.bg_focus
|
||||
theme.fg_normal = custom.fg_normal or beautiful.menu_fg_normal or beautiful.fg_normal
|
||||
theme.bg_normal = custom.bg_normal or beautiful.menu_bg_normal or beautiful.bg_normal
|
||||
|
||||
theme.submenu_icon = custom.submenu_icon or beautiful.menu_submenu_icon
|
||||
|
||||
theme.menu_height = custom.height or beautiful.menu_height or 16
|
||||
theme.menu_width = custom.width or beautiful.menu_width or 100
|
||||
|
||||
theme.border = custom.border_color or beautiful.menu_border_color or beautiful.border_normal
|
||||
theme.border_width = custom.border_width or beautiful.menu_border_width or beautiful.border_width
|
||||
|
||||
return theme
|
||||
local function load_theme(a, b)
|
||||
a = a or {}
|
||||
b = b or {}
|
||||
local ret = {}
|
||||
local fallback = beautiful.get()
|
||||
if a.reset then b = fallback end
|
||||
if a == "reset" then a = fallback end
|
||||
ret.border = a.border_color or b.menu_border_color or b.border_normal or
|
||||
fallback.menu_border_color or fallback.border_normal
|
||||
ret.border_width= a.border_width or b.menu_border_width or b.border_width or
|
||||
fallback.menu_border_width or fallback.border_width
|
||||
ret.fg_focus = a.fg_focus or b.menu_fg_focus or b.fg_focus or
|
||||
fallback.menu_fg_focus or fallback.fg_focus
|
||||
ret.bg_focus = a.bg_focus or b.menu_bg_focus or b.bg_focus or
|
||||
fallback.menu_bg_focus or fallback.bg_focus
|
||||
ret.fg_normal = a.fg_normal or b.menu_fg_normal or b.fg_normal or
|
||||
fallback.menu_fg_normal or fallback.fg_normal
|
||||
ret.bg_normal = a.bg_normal or b.menu_bg_normal or b.bg_normal or
|
||||
fallback.menu_bg_normal or fallback.bg_normal
|
||||
ret.submenu_icon= a.submenu_icon or b.menu_submenu_icon or b.submenu_icon or
|
||||
fallback.menu_submenu_icon or fallback.submenu_icon
|
||||
ret.height = a.height or b.menu_height or b.height or
|
||||
fallback.menu_height or 16
|
||||
ret.width = a.width or b.menu_width or b.width or
|
||||
fallback.menu_width or 100
|
||||
return ret
|
||||
end
|
||||
|
||||
local function item_leave(menu, num)
|
||||
if num > 0 then
|
||||
menu.items[num].wibox:set_fg(menu.theme.fg_normal)
|
||||
menu.items[num].wibox:set_bg(menu.theme.bg_normal)
|
||||
|
||||
local function item_position(menu, child)
|
||||
local in_dir, other, a, b = 0, 0, "height", "width"
|
||||
local dir = menu.layout.get_dir and menu.layout:get_dir() or "y"
|
||||
if dir == "x" then a, b = b, a end
|
||||
|
||||
local in_dir, other = 0, menu[b]
|
||||
local num = util.table.hasitem(menu.child, child)
|
||||
if num then
|
||||
for i = 0, num - 1 do
|
||||
local item = menu.items[i]
|
||||
if item then
|
||||
other = math.max(other, item[b])
|
||||
in_dir = in_dir + item[a]
|
||||
end
|
||||
end
|
||||
end
|
||||
local w, h = other, in_dir
|
||||
if dir == "x" then w, h = h, w end
|
||||
return w, h
|
||||
end
|
||||
|
||||
--- Hide a menu popup.
|
||||
-- @param menu The menu to hide.
|
||||
function hide(menu)
|
||||
-- Remove items from screen
|
||||
for i = 1, #menu.items do
|
||||
item_leave(menu, i)
|
||||
menu.items[i].wibox.visible = false
|
||||
end
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
menu.sel = nil
|
||||
|
||||
if cur_menu == menu then
|
||||
cur_menu = cur_menu.parent
|
||||
end
|
||||
if not cur_menu and menu.keygrabber then
|
||||
capi.keygrabber.stop()
|
||||
end
|
||||
end
|
||||
local function set_coords(menu, screen_idx, m_coords)
|
||||
local s_geometry = capi.screen[screen_idx].workarea
|
||||
local screen_w = s_geometry.x + s_geometry.width
|
||||
local screen_h = s_geometry.y + s_geometry.height
|
||||
|
||||
menu.width = menu.wibox.width
|
||||
menu.height = menu.wibox.height
|
||||
|
||||
menu.x = menu.wibox.x
|
||||
menu.y = menu.wibox.y
|
||||
|
||||
-- Get the elder parent so for example when you kill
|
||||
-- it, it will destroy the whole family.
|
||||
local function get_parents(menu)
|
||||
if menu.parent then
|
||||
return get_parents(menu.parent)
|
||||
local w, h = item_position(menu.parent, menu)
|
||||
w = w + menu.parent.theme.border_width
|
||||
h = h + menu.parent.theme.border_width
|
||||
|
||||
menu.y = menu.parent.y + h + menu.height > screen_h and
|
||||
screen_h - menu.height or menu.parent.y + h
|
||||
menu.x = menu.parent.x + w + menu.width > screen_w and
|
||||
menu.parent.x - menu.width or menu.parent.x + w
|
||||
else
|
||||
if m_coords == nil then
|
||||
m_coords = capi.mouse.coords()
|
||||
m_coords.x = m_coords.x + 1
|
||||
m_coords.y = m_coords.y + 1
|
||||
end
|
||||
menu.y = m_coords.y < s_geometry.y and s_geometry.y or m_coords.y
|
||||
menu.x = m_coords.x < s_geometry.x and s_geometry.x or m_coords.x
|
||||
|
||||
menu.y = menu.y + menu.height > screen_h and
|
||||
screen_h - menu.height or menu.y
|
||||
menu.x = menu.x + menu.width > screen_w and
|
||||
screen_w - menu.width or menu.x
|
||||
end
|
||||
return menu
|
||||
|
||||
menu.wibox.x = menu.x
|
||||
menu.wibox.y = menu.y
|
||||
end
|
||||
|
||||
local function exec(menu, num, mouse_event)
|
||||
local cmd = menu.items[num].cmd
|
||||
if type(cmd) == "table" then
|
||||
if #cmd == 0 then
|
||||
return
|
||||
end
|
||||
if not menu.child[num] then
|
||||
menu.child[num] = new({ items = cmd }, menu, num)
|
||||
end
|
||||
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
menu.active_child = menu.child[num]
|
||||
menu.active_child:show()
|
||||
elseif type(cmd) == "string" then
|
||||
get_parents(menu):hide()
|
||||
util.spawn(cmd)
|
||||
elseif type(cmd) == "function" then
|
||||
get_parents(menu):hide()
|
||||
cmd(menu.items[num].returned_value)
|
||||
local function set_size(menu)
|
||||
local in_dir, other, a, b = 0, 0, "height", "width"
|
||||
local dir = menu.layout.get_dir and menu.layout:get_dir() or "y"
|
||||
if dir == "x" then a, b = b, a end
|
||||
for _, item in ipairs(menu.items) do
|
||||
other = math.max(other, item[b])
|
||||
in_dir = in_dir + item[a]
|
||||
end
|
||||
menu[a], menu[b] = in_dir, other
|
||||
if in_dir > 0 and other > 0 then
|
||||
menu.wibox[a] = in_dir
|
||||
menu.wibox[b] = other
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function item_enter(menu, num, mouse_event)
|
||||
if menu.sel == num then
|
||||
return
|
||||
elseif menu.sel then
|
||||
item_leave(menu, menu.sel)
|
||||
end
|
||||
|
||||
menu.items[num].wibox:set_fg(menu.theme.fg_focus)
|
||||
menu.items[num].wibox:set_bg(menu.theme.bg_focus)
|
||||
menu.sel = num
|
||||
cur_menu = menu
|
||||
|
||||
if menu.auto_expand and mouse_event then
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
|
||||
if type(menu.items[num].cmd) == "table" then
|
||||
exec(menu, num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function check_access_key(menu, key)
|
||||
for i, item in pairs(menu.items) do
|
||||
for i, item in ipairs(menu.items) do
|
||||
if item.akey == key then
|
||||
item_enter(menu, i)
|
||||
exec(menu, i)
|
||||
return
|
||||
menu:item_enter(i)
|
||||
menu:exec(i)
|
||||
return
|
||||
end
|
||||
end
|
||||
if menu.parent then
|
||||
|
@ -165,6 +185,7 @@ local function check_access_key(menu, key)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
local function grabber(mod, key, event)
|
||||
if event == "release" then
|
||||
return true
|
||||
|
@ -173,16 +194,16 @@ local function grabber(mod, key, event)
|
|||
local sel = cur_menu.sel or 0
|
||||
if util.table.hasitem(menu_keys.up, key) then
|
||||
local sel_new = sel-1 < 1 and #cur_menu.items or sel-1
|
||||
item_enter(cur_menu, sel_new)
|
||||
cur_menu:item_enter(sel_new)
|
||||
elseif util.table.hasitem(menu_keys.down, key) then
|
||||
local sel_new = sel+1 > #cur_menu.items and 1 or sel+1
|
||||
item_enter(cur_menu, sel_new)
|
||||
cur_menu:item_enter(sel_new)
|
||||
elseif sel > 0 and util.table.hasitem(menu_keys.exec, key) then
|
||||
exec(cur_menu, sel)
|
||||
cur_menu:exec(sel)
|
||||
elseif util.table.hasitem(menu_keys.back, key) then
|
||||
cur_menu:hide()
|
||||
elseif util.table.hasitem(menu_keys.close, key) then
|
||||
get_parents(cur_menu):hide()
|
||||
get_root(cur_menu):hide()
|
||||
else
|
||||
check_access_key(cur_menu, key)
|
||||
end
|
||||
|
@ -190,182 +211,93 @@ local function grabber(mod, key, event)
|
|||
return true
|
||||
end
|
||||
|
||||
local function add_item(data, num, item_info)
|
||||
local item = wibox({
|
||||
fg = data.theme.fg_normal,
|
||||
bg = data.theme.bg_normal,
|
||||
border_color = data.theme.border,
|
||||
border_width = data.theme.border_width,
|
||||
type = "popup_menu"
|
||||
})
|
||||
|
||||
-- Create bindings
|
||||
local bindings = util.table.join(
|
||||
button({}, 1, function () item_enter(data, num); exec(data, num) end),
|
||||
button({}, 3, function () data:hide() end)
|
||||
)
|
||||
function exec(menu, num, mouse_event)
|
||||
local item = menu.items[num]
|
||||
if not item then return end
|
||||
local cmd = item.cmd
|
||||
if type(cmd) == "table" then
|
||||
if #cmd == 0 then
|
||||
return
|
||||
end
|
||||
if not menu.child[num] then
|
||||
menu.child[num] = new(cmd, menu)
|
||||
end
|
||||
|
||||
-- Create the item label widget
|
||||
local label = wibox.widget.textbox()
|
||||
local key = ''
|
||||
label:set_markup(string.gsub(util.escape(item_info[1]), "&(%w)",
|
||||
function (l)
|
||||
key = string.lower(l)
|
||||
return "<u>"..l.."</u>"
|
||||
end, 1))
|
||||
-- Set icon if needed
|
||||
local iconbox
|
||||
local margin = wibox.layout.margin()
|
||||
margin:set_widget(label)
|
||||
if item_info[3] then
|
||||
local icon = type(item_info[3]) == "string" and capi.oocairo.image_surface_create_from_png(item_info[3]) or item_info[3]
|
||||
local width = icon:get_width()
|
||||
local height = icon:get_height()
|
||||
if width > data.h or height > data.h then
|
||||
local w, h
|
||||
if ((data.h/height) * width) > data.h then
|
||||
w, h = data.h, (data.h / width) * height
|
||||
else
|
||||
w, h = (data.h / height) * width, data.h
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
menu.active_child = menu.child[num]
|
||||
menu.active_child:show()
|
||||
elseif type(cmd) == "string" then
|
||||
get_root(menu):hide()
|
||||
util.spawn(cmd)
|
||||
elseif type(cmd) == "function" then
|
||||
local visible, action = cmd(item, menu)
|
||||
if not visible then
|
||||
get_root(menu):hide()
|
||||
else
|
||||
menu:update()
|
||||
if menu.items[num] then
|
||||
menu:item_enter(num, mouse_event)
|
||||
end
|
||||
-- We need to scale the image to size w x h
|
||||
local img = capi.oocairo.image_surface_create("argb32", w, h)
|
||||
local cr = capi.oocairo.context_create(img)
|
||||
cr:scale(w / width, h / height)
|
||||
cr:set_source(icon, 0, 0)
|
||||
cr:paint()
|
||||
icon = img
|
||||
end
|
||||
iconbox = wibox.widget.imagebox()
|
||||
margin:set_left(2)
|
||||
if not iconbox:set_image(icon) then
|
||||
-- Setting the image failed, it must be invalid
|
||||
iconbox = nil
|
||||
if action and type(action) == "function" then
|
||||
action()
|
||||
end
|
||||
end
|
||||
if not iconbox then
|
||||
margin:set_left(data.h + 2)
|
||||
end
|
||||
|
||||
item:buttons(bindings)
|
||||
|
||||
local mouse_enter_func = function () item_enter(data, num, true) end
|
||||
item:connect_signal("mouse::enter", mouse_enter_func)
|
||||
|
||||
-- Create the submenu icon widget
|
||||
local submenu
|
||||
if type(item_info[2]) == "table" then
|
||||
submenu = wibox.widget.imagebox()
|
||||
if data.theme.submenu_icon then
|
||||
submenu:set_image(capi.oocairo.image_surface_create_from_png(data.theme.submenu_icon))
|
||||
end
|
||||
submenu:buttons(bindings)
|
||||
end
|
||||
|
||||
-- Add widgets to the wibox
|
||||
local left = wibox.layout.fixed.horizontal()
|
||||
if iconbox then
|
||||
left:add(iconbox)
|
||||
end
|
||||
-- This contains the label
|
||||
left:add(margin)
|
||||
|
||||
local layout = wibox.layout.align.horizontal()
|
||||
layout:set_middle(left)
|
||||
if submenu then
|
||||
layout:set_right(submenu)
|
||||
end
|
||||
|
||||
item:set_widget(layout)
|
||||
|
||||
local w, h = label:fit(0, 0)
|
||||
item.height = h + 2
|
||||
item.ontop = true
|
||||
|
||||
return { wibox = item, akey= key, cmd = item_info[2], returned_value=item_info[1] }
|
||||
end
|
||||
|
||||
--- Build a popup menu with running clients and shows it.
|
||||
-- @param menu Menu table, see new() function for more informations
|
||||
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
|
||||
-- @return The menu.
|
||||
function clients(menu, args)
|
||||
local cls = capi.client.get()
|
||||
local cls_t = {}
|
||||
for k, c in pairs(cls) do
|
||||
cls_t[#cls_t + 1] = { util.escape(c.name) or "",
|
||||
function ()
|
||||
if not c:isvisible() then
|
||||
tags.viewmore(c:tags(), c.screen)
|
||||
end
|
||||
capi.client.focus = c
|
||||
c:raise()
|
||||
end,
|
||||
c.icon }
|
||||
function item_enter(menu, num, mouse_event)
|
||||
local item = menu.items[num]
|
||||
if num == nil or menu.sel == num or not item then
|
||||
return
|
||||
elseif menu.sel then
|
||||
menu:item_leave(menu.sel)
|
||||
end
|
||||
--print("sel", num, menu.sel, item.theme.bg_focus)
|
||||
item._background:set_fg(item.theme.fg_focus)
|
||||
item._background:set_bg(item.theme.bg_focus)
|
||||
cur_menu = menu
|
||||
menu.sel = num
|
||||
|
||||
if not menu then
|
||||
menu = {}
|
||||
end
|
||||
|
||||
menu.items = cls_t
|
||||
|
||||
local m = new(menu)
|
||||
m:show(args)
|
||||
return m
|
||||
end
|
||||
|
||||
local function set_coords(menu, screen_idx, m_coords)
|
||||
local s_geometry = capi.screen[screen_idx].workarea
|
||||
local screen_w = s_geometry.x + s_geometry.width
|
||||
local screen_h = s_geometry.y + s_geometry.height
|
||||
|
||||
local i_h = menu.h + menu.theme.border_width
|
||||
local m_h = (i_h * #menu.items) + menu.theme.border_width
|
||||
|
||||
if menu.parent then
|
||||
menu.w = menu.parent.w
|
||||
menu.h = menu.parent.h
|
||||
|
||||
local p_w = i_h * (menu.num - 1)
|
||||
local m_w = menu.w - menu.theme.border_width
|
||||
|
||||
menu.y = menu.parent.y + p_w + m_h > screen_h and screen_h - m_h or menu.parent.y + p_w
|
||||
menu.x = menu.parent.x + m_w*2 > screen_w and menu.parent.x - m_w or menu.parent.x + m_w
|
||||
else
|
||||
local m_w = menu.w
|
||||
if m_coords == nil then
|
||||
m_coords = capi.mouse.coords()
|
||||
m_coords.x = m_coords.x + 1
|
||||
m_coords.y = m_coords.y + 1
|
||||
if menu.auto_expand and mouse_event then
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
|
||||
menu.y = m_coords.y < s_geometry.y and s_geometry.y or m_coords.y
|
||||
menu.x = m_coords.x < s_geometry.x and s_geometry.x or m_coords.x
|
||||
|
||||
menu.y = menu.y + m_h > screen_h and screen_h - m_h or menu.y
|
||||
menu.x = menu.x + m_w > screen_w and screen_w - m_w or menu.x
|
||||
if type(item.cmd) == "table" then
|
||||
menu:exec(num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function item_leave(menu, num)
|
||||
--print("leave", num)
|
||||
local item = menu.items[num]
|
||||
if item then
|
||||
item._background:set_fg(item.theme.fg_normal)
|
||||
item._background:set_bg(item.theme.bg_normal)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Show a menu.
|
||||
-- @param menu The menu to show.
|
||||
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
|
||||
-- @param args.coords Menu position defaulting to mouse.coords()
|
||||
function show(menu, args)
|
||||
args = args or {}
|
||||
local coords = args.coords or nil
|
||||
local screen_index = capi.mouse.screen
|
||||
local keygrabber = args.keygrabber or false
|
||||
local coords = args.coords or nil
|
||||
|
||||
menu.wibox.screen = screen_index
|
||||
if not set_size(menu) then return end
|
||||
set_coords(menu, screen_index, coords)
|
||||
for num, item in pairs(menu.items) do
|
||||
local wibox = item.wibox
|
||||
wibox.width = menu.w
|
||||
wibox.height = menu.h
|
||||
wibox.x = menu.x
|
||||
wibox.y = menu.y + (num - 1) * (menu.h + wibox.border_width)
|
||||
wibox.visible = true
|
||||
end
|
||||
|
||||
if menu.parent then
|
||||
menu.keygrabber = menu.parent.keygrabber
|
||||
|
@ -379,6 +311,29 @@ function show(menu, args)
|
|||
capi.keygrabber.run(grabber)
|
||||
end
|
||||
cur_menu = menu
|
||||
menu.wibox.visible = true
|
||||
end
|
||||
|
||||
--- Hide a menu popup.
|
||||
-- @param menu The menu to hide.
|
||||
function hide(menu)
|
||||
-- Remove items from screen
|
||||
for i = 1, #menu.items do
|
||||
menu:item_leave(i)
|
||||
end
|
||||
if menu.active_child then
|
||||
menu.active_child:hide()
|
||||
menu.active_child = nil
|
||||
end
|
||||
menu.sel = nil
|
||||
|
||||
if cur_menu == menu then
|
||||
cur_menu = cur_menu.parent
|
||||
end
|
||||
if not cur_menu and menu.keygrabber then
|
||||
capi.keygrabber.stop()
|
||||
end
|
||||
menu.wibox.visible = false
|
||||
end
|
||||
|
||||
--- Toggle menu visibility.
|
||||
|
@ -386,22 +341,254 @@ end
|
|||
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
|
||||
-- @param args.coords Menu position {x,y}
|
||||
function toggle(menu, args)
|
||||
if menu.items[1] and menu.items[1].wibox.visible then
|
||||
if menu.wibox.visible then
|
||||
menu:hide()
|
||||
else
|
||||
menu:show(args)
|
||||
end
|
||||
end
|
||||
|
||||
--- Open a menu popup.
|
||||
-- @param menu Table containing the menu informations.<br/>
|
||||
--- Update menu content
|
||||
-- @param menu The mnenu to update.
|
||||
function update(menu)
|
||||
if menu.wibox.visible then
|
||||
menu:show({
|
||||
keygrabber = menu.keygrabber,
|
||||
coords = { x = menu.x, y = menu.y } })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Get the elder parent so for example when you kill
|
||||
-- it, it will destroy the whole family.
|
||||
-- @param menu The sub menu of the menu family.
|
||||
function get_root(menu)
|
||||
return menu.parent and get_root(menu.parent) or menu
|
||||
end
|
||||
|
||||
--- Add a new menu entry
|
||||
-- @param menu The parent menu
|
||||
-- @param args The item params
|
||||
-- @param args.new (Default: awful.menu.entry) The menu entry constructor
|
||||
-- @param args.theme (Optional) The menu entry theme
|
||||
-- @param args.* params needed for the menu entry constructor
|
||||
-- @param index (Optional) the index where the new entry will inserted
|
||||
function add(menu, args, index)
|
||||
if not args then return end
|
||||
local theme = load_theme(args.theme or {}, menu.theme)
|
||||
args.theme = theme
|
||||
args.new = args.new or entry
|
||||
local success, item = pcall(args.new, menu, args)
|
||||
if not success then
|
||||
print("Error while creating menu entry: " .. item)
|
||||
return
|
||||
end
|
||||
if not item.widget then
|
||||
print("Error while checking menu entry: no property widget found.")
|
||||
return
|
||||
end
|
||||
item.parent = menu
|
||||
item.theme = item.theme or theme
|
||||
item.width = item.width or theme.width
|
||||
item.height = item.height or theme.height
|
||||
wibox.widget.base.check_widget(item.widget)
|
||||
item._background = wibox.widget.background()
|
||||
item._background:set_widget(item.widget)
|
||||
item._background:set_fg(item.theme.fg_normal)
|
||||
item._background:set_bg(item.theme.bg_normal)
|
||||
|
||||
|
||||
-- Create bindings
|
||||
item._background:buttons(util.table.join(
|
||||
button({}, 3, function () menu:hide() end),
|
||||
button({}, 1, function ()
|
||||
local num = util.table.hasitem(menu.items, item)
|
||||
menu:item_enter(num)
|
||||
menu:exec(num)
|
||||
end )))
|
||||
|
||||
|
||||
item._mouse = function ()
|
||||
local num = util.table.hasitem(menu.items, item)
|
||||
menu:item_enter(num, true)
|
||||
end
|
||||
item.widget:connect_signal("mouse::enter", item._mouse)
|
||||
|
||||
if index then
|
||||
menu.layout:reset()
|
||||
table.insert(menu.items, index, item)
|
||||
for _, i in ipairs(menu.items) do
|
||||
menu.layout:add(i._background)
|
||||
end
|
||||
else
|
||||
table.insert(menu.items, item)
|
||||
menu.layout:add(item._background)
|
||||
end
|
||||
return item
|
||||
end
|
||||
|
||||
-- Delete menu entry at given position
|
||||
-- @param menu The menu
|
||||
-- @param num The position in the table of the menu entry to be deleted; can be also the menu entry itself
|
||||
function delete(menu, num)
|
||||
if type(num) == "table" then
|
||||
num = util.table.hasitem(menu.items, num)
|
||||
end
|
||||
local item = menu.items[num]
|
||||
if not item then return end
|
||||
item.widget:disconnect_signal("mouse::enter", item._mouse)
|
||||
item.widget.screen = nil
|
||||
table.remove(menu.items, num)
|
||||
if menu.sel == num then
|
||||
menu:item_leave(menu.sel)
|
||||
menu.sel = nil
|
||||
end
|
||||
menu.layout:reset()
|
||||
for _, i in ipairs(menu.items) do
|
||||
menu.layout:add(i._background)
|
||||
end
|
||||
if menu.child[num] then
|
||||
menu.child[num]:hide()
|
||||
if menu.active_child == menu.child[num] then
|
||||
menu.active_child = nil
|
||||
end
|
||||
table.remove(menu.child, num)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--- Build a popup menu with running clients and shows it.
|
||||
-- @param menu Menu table, see new() function for more informations
|
||||
-- @param args.keygrabber A boolean enabling or not the keyboard navigation.
|
||||
-- @return The menu.
|
||||
function clients(menu, args) -- FIXME crude api
|
||||
menu = menu or {}
|
||||
local cls = capi.client.get()
|
||||
local cls_t = {}
|
||||
for k, c in pairs(cls) do
|
||||
cls_t[#cls_t + 1] = {
|
||||
util.escape(c.name) or "",
|
||||
function ()
|
||||
if not c:isvisible() then
|
||||
tags.viewmore(c:tags(), c.screen)
|
||||
end
|
||||
capi.client.focus = c
|
||||
c:raise()
|
||||
end,
|
||||
c.icon }
|
||||
end
|
||||
args = args or {}
|
||||
args.items = args.items or {}
|
||||
table_merge(args.items, cls_t)
|
||||
|
||||
local m = new(args)
|
||||
m:show(args)
|
||||
return m
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--- Default awful.menu.entry constructor
|
||||
-- @param parent The parent menu
|
||||
-- @param args the item params
|
||||
-- @return table with 'widget', 'cmd', 'akey' and all the properties the user wants to change
|
||||
function entry(parent, args)
|
||||
args = args or {}
|
||||
args.text = args[1] or args.text or ""
|
||||
args.cmd = args[2] or args.cmd
|
||||
args.icon = args[3] or args.icon
|
||||
local ret = {}
|
||||
-- Create the item label widget
|
||||
local label = wibox.widget.textbox()
|
||||
local key = ''
|
||||
label:set_markup(string.gsub(
|
||||
util.escape(args.text), "&(%w)",
|
||||
function (l)
|
||||
key = string.lower(l)
|
||||
return "<u>" .. l .. "</u>"
|
||||
end, 1))
|
||||
-- Set icon if needed
|
||||
local icon, iconbox
|
||||
local margin = wibox.layout.margin()
|
||||
margin:set_widget(label)
|
||||
if args.icon then
|
||||
icon = args.icon
|
||||
if type(icon) == "string" then
|
||||
icon = capi.oocairo.image_surface_create_from_png(icon)
|
||||
end
|
||||
end
|
||||
if icon then
|
||||
local iw = icon:get_width()
|
||||
local ih = icon:get_height()
|
||||
if iw > args.theme.width or ih > args.theme.height then
|
||||
local w, h
|
||||
if ((args.theme.height / ih) * iw) > args.theme.width then
|
||||
w, h = args.theme.height, (args.theme.height / iw) * ih
|
||||
else
|
||||
w, h = (args.theme.height / ih) * iw, args.theme.height
|
||||
end
|
||||
-- We need to scale the image to size w x h
|
||||
local img = capi.oocairo.image_surface_create("argb32", w, h)
|
||||
local cr = capi.oocairo.context_create(img)
|
||||
cr:scale(w / iw, h / ih)
|
||||
cr:set_source(icon, 0, 0)
|
||||
cr:paint()
|
||||
icon = img
|
||||
end
|
||||
iconbox = wibox.widget.imagebox()
|
||||
if iconbox:set_image(icon) then
|
||||
margin:set_left(2)
|
||||
else
|
||||
iconbox = nil
|
||||
end
|
||||
end
|
||||
if not iconbox then
|
||||
margin:set_left(args.theme.height + 2)
|
||||
end
|
||||
-- Create the submenu icon widget
|
||||
local submenu_icon
|
||||
if type(args.cmd) == "table" then
|
||||
submenu_icon = wibox.widget.imagebox()
|
||||
if args.theme.submenu_icon then
|
||||
submenu_icon:set_image(
|
||||
capi.oocairo.image_surface_create_from_png(
|
||||
args.theme.submenu_icon))
|
||||
end
|
||||
end
|
||||
-- Add widgets to the wibox
|
||||
local left = wibox.layout.fixed.horizontal()
|
||||
if iconbox then
|
||||
left:add(iconbox)
|
||||
end
|
||||
-- This contains the label
|
||||
left:add(margin)
|
||||
|
||||
local layout = wibox.layout.align.horizontal()
|
||||
layout:set_middle(left)
|
||||
if submenu_icon then
|
||||
layout:set_right(submenu_icon)
|
||||
end
|
||||
|
||||
return table_update(ret, {
|
||||
label = label,
|
||||
icon = iconbox,
|
||||
widget = layout,
|
||||
cmd = args.cmd,
|
||||
akey = key,
|
||||
})
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--- Create a menu popup.
|
||||
-- @param args Table containing the menu informations.<br/>
|
||||
-- <ul>
|
||||
-- <li> Key items: Table containing the displayed items. Each element is a table containing: item name, triggered action, submenu table or function, item icon (optional). </li>
|
||||
-- <li> Keys [fg|bg]_[focus|normal], border, border_width, submenu_icon, height and width override the default display for your menu, each of them are optional. </li>
|
||||
-- <li> Key items: Table containing the displayed items. Each element is a table by default (when element 'new' is awful.menu.entry) containing: item name, triggered action, submenu table or function, item icon (optional). </li>
|
||||
-- <li> Keys theme.[fg|bg]_[focus|normal], theme.border_color, theme.border_width, theme.submenu_icon, theme.height and theme.width override the default display for your menu and/or of your menu entry, each of them are optional. </li>
|
||||
-- <li> Key auto_expand controls the submenu auto expand behaviour by setting it to true (default) or false. </li>
|
||||
-- </ul>
|
||||
-- @param parent Specify the parent menu if we want to open a submenu, this value should never be set by the user.
|
||||
-- @param num Specify the parent's clicked item number if we want to open a submenu, this value should never be set by the user.
|
||||
-- @usage The following function builds, and shows a menu of clients that match
|
||||
-- a particular rule. Bound to a key, it can for example be used to select from
|
||||
-- dozens of terminals open on several tags. With the use of
|
||||
|
@ -423,49 +610,61 @@ end
|
|||
-- } <br/>
|
||||
-- end <br/>
|
||||
-- end <br/>
|
||||
-- m = awful.menu({items = terms}) <br/>
|
||||
-- m = awful.menu(terms) <br/>
|
||||
-- m:show({keygrabber=true}) <br/>
|
||||
-- return m <br/>
|
||||
-- end <br/>
|
||||
--</code></p>
|
||||
function new(menu, parent, num)
|
||||
-- Create a table to store our menu informations
|
||||
local data = {}
|
||||
function new(args, parent)
|
||||
args = args or {}
|
||||
args.layout = args.layout or wibox.layout.flex.vertical
|
||||
local menu = table_update(object(), {
|
||||
item_enter = item_enter,
|
||||
item_leave = item_leave,
|
||||
get_root = get_root,
|
||||
delete = delete,
|
||||
update = update,
|
||||
toggle = toggle,
|
||||
hide = hide,
|
||||
show = show,
|
||||
exec = exec,
|
||||
add = add,
|
||||
child = {},
|
||||
items = {},
|
||||
parent = parent,
|
||||
layout = args.layout(),
|
||||
theme = load_theme(args.theme or {}, parent and parent.theme) })
|
||||
|
||||
data.items = {}
|
||||
data.num = num or 1
|
||||
data.theme = parent and parent.theme or load_theme(menu)
|
||||
data.parent = parent
|
||||
data.child = {}
|
||||
if parent then
|
||||
data.auto_expand = parent.auto_expand
|
||||
elseif menu.auto_expand ~= nil then
|
||||
data.auto_expand = menu.auto_expand
|
||||
menu.auto_expand = parent.auto_expand
|
||||
elseif args.auto_expand ~= nil then
|
||||
menu.auto_expand = args.auto_expand
|
||||
else
|
||||
data.auto_expand = true
|
||||
menu.auto_expand = true
|
||||
end
|
||||
data.h = parent and parent.h or data.theme.menu_height
|
||||
if type(data.h) ~= 'number' then data.h = tonumber(data.h) end
|
||||
data.w = parent and parent.w or data.theme.menu_width
|
||||
if type(data.w) ~= 'number' then data.w = tonumber(data.w) end
|
||||
|
||||
-- Create items
|
||||
for k, v in pairs(menu.items) do
|
||||
table.insert(data.items, add_item(data, k, v))
|
||||
for i, v in ipairs(args) do menu:add(v) end
|
||||
if args.items then
|
||||
for i, v in pairs(args.items) do menu:add(v) end
|
||||
end
|
||||
|
||||
if #data.items > 0 and data.h < data.items[1].wibox.height then
|
||||
data.h = data.items[1].wibox.height
|
||||
end
|
||||
menu.wibox = wibox({
|
||||
ontop = true,
|
||||
fg = menu.theme.fg_normal,
|
||||
bg = menu.theme.bg_normal,
|
||||
border_color = menu.theme.border,
|
||||
border_width = menu.theme.border_width,
|
||||
type = "popup_menu" })
|
||||
menu.wibox.visible = false
|
||||
menu.wibox:set_widget(menu.layout)
|
||||
set_size(menu)
|
||||
|
||||
-- Set methods
|
||||
data.hide = hide
|
||||
data.show = show
|
||||
data.toggle = toggle
|
||||
|
||||
return data
|
||||
menu.x = menu.wibox.x
|
||||
menu.y = menu.wibox.y
|
||||
return menu
|
||||
end
|
||||
|
||||
setmetatable(_M, { __call = function(_, ...) return new(...) end })
|
||||
setmetatable(_M, { __call = function (_, ...) return new(...) end })
|
||||
|
||||
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|
||||
|
|
Loading…
Reference in New Issue