diff options
Diffstat (limited to 'awesome/lib/awful/menu.lua')
-rw-r--r-- | awesome/lib/awful/menu.lua | 723 |
1 files changed, 0 insertions, 723 deletions
diff --git a/awesome/lib/awful/menu.lua b/awesome/lib/awful/menu.lua deleted file mode 100644 index dcbc5fa..0000000 --- a/awesome/lib/awful/menu.lua +++ /dev/null @@ -1,723 +0,0 @@ --------------------------------------------------------------------------------- ---- A menu for awful --- --- @author Damien Leone <damien.leone@gmail.com> --- @author Julien Danjou <julien@danjou.info> --- @author dodo --- @copyright 2008, 2011 Damien Leone, Julien Danjou, dodo --- @module awful.menu --------------------------------------------------------------------------------- - -local wibox = require("wibox") -local button = require("awful.button") -local util = require("awful.util") -local spawn = require("awful.spawn") -local tags = require("awful.tag") -local keygrabber = require("awful.keygrabber") -local client_iterate = require("awful.client").iterate -local beautiful = require("beautiful") -local dpi = require("beautiful").xresources.apply_dpi -local object = require("gears.object") -local surface = require("gears.surface") -local protected_call = require("gears.protected_call") -local cairo = require("lgi").cairo -local setmetatable = setmetatable -local tonumber = tonumber -local string = string -local ipairs = ipairs -local pairs = pairs -local print = print -local table = table -local type = type -local math = math -local capi = { - screen = screen, - mouse = mouse, - client = client } -local screen = require("awful.screen") - - -local menu = { mt = {} } - - -local table_update = function (t, set) - for k, v in pairs(set) do - t[k] = v - end - return t -end - ---- The icon used for sub-menus. --- @beautiful beautiful.menu_submenu_icon - ---- The item height. --- @beautiful beautiful.menu_height --- @tparam[opt=16] number menu_height - ---- The default menu width. --- @beautiful beautiful.menu_width --- @tparam[opt=100] number menu_width - ---- The menu item border color. --- @beautiful beautiful.menu_border_color --- @tparam[opt=0] number menu_border_color - ---- The menu item border width. --- @beautiful beautiful.menu_border_width --- @tparam[opt=0] number menu_border_width - ---- The default focused item foreground (text) color. --- @beautiful beautiful.menu_fg_focus --- @param color --- @see gears.color - ---- The default focused item background color. --- @beautiful beautiful.menu_bg_focus --- @param color --- @see gears.color - ---- The default foreground (text) color. --- @beautiful beautiful.menu_fg_normal --- @param color --- @see gears.color - ---- The default background color. --- @beautiful beautiful.menu_bg_normal --- @param color --- @see gears.color - ---- The default sub-menu indicator if no menu_submenu_icon is provided. --- @beautiful beautiful.menu_submenu --- @tparam[opt="▶"] string menu_submenu The sub-menu text. --- @see beautiful.menu_submenu_icon - ---- Key bindings for menu navigation. --- Keys are: up, down, exec, enter, back, close. Value are table with a list of valid --- keys for the action, i.e. menu_keys.up = { "j", "k" } will bind 'j' and 'k' --- key to up action. This is common to all created menu. --- @class table --- @name menu_keys -menu.menu_keys = { up = { "Up", "k" }, - down = { "Down", "j" }, - back = { "Left", "h" }, - exec = { "Return" }, - enter = { "Right", "l" }, - close = { "Escape" } } - - -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 or 0 - 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.submenu = a.submenu or b.menu_submenu or b.submenu or - fallback.menu_submenu or fallback.submenu or "▶" - 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 - ret.font = a.font or b.font or fallback.font - for _, prop in ipairs({"width", "height", "menu_width"}) do - if type(ret[prop]) ~= "number" then ret[prop] = tonumber(ret[prop]) end - end - return ret -end - - -local function item_position(_menu, child) - local a, b = "height", "width" - local dir = _menu.layout.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 - - -local function set_coords(_menu, s, m_coords) - local s_geometry = s.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 - - if _menu.parent then - local w, h = item_position(_menu.parent, _menu) - w = w + _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 - - _menu.wibox.x = _menu.x - _menu.wibox.y = _menu.y -end - - -local function set_size(_menu) - local in_dir, other, a, b = 0, 0, "height", "width" - local dir = _menu.layout.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 check_access_key(_menu, key) - for i, item in ipairs(_menu.items) do - if item.akey == key then - _menu:item_enter(i) - _menu:exec(i, { exec = true }) - return - end - end - if _menu.parent then - check_access_key(_menu.parent, key) - end -end - - -local function grabber(_menu, _, key, event) - if event ~= "press" then return end - - local sel = _menu.sel or 0 - if util.table.hasitem(menu.menu_keys.up, key) then - local sel_new = sel-1 < 1 and #_menu.items or sel-1 - _menu:item_enter(sel_new) - elseif util.table.hasitem(menu.menu_keys.down, key) then - local sel_new = sel+1 > #_menu.items and 1 or sel+1 - _menu:item_enter(sel_new) - elseif sel > 0 and util.table.hasitem(menu.menu_keys.enter, key) then - _menu:exec(sel) - elseif sel > 0 and util.table.hasitem(menu.menu_keys.exec, key) then - _menu:exec(sel, { exec = true }) - elseif util.table.hasitem(menu.menu_keys.back, key) then - _menu:hide() - elseif util.table.hasitem(menu.menu_keys.close, key) then - menu.get_root(_menu):hide() - else - check_access_key(_menu, key) - end -end - - -function menu:exec(num, opts) - opts = opts or {} - local item = self.items[num] - if not item then return end - local cmd = item.cmd - if type(cmd) == "table" then - local action = cmd.cmd - if #cmd == 0 then - if opts.exec and action and type(action) == "function" then - action() - end - return - end - if not self.child[num] then - self.child[num] = menu.new(cmd, self) - end - local can_invoke_action = opts.exec and - action and type(action) == "function" and - (not opts.mouse or (opts.mouse and (self.auto_expand or - (self.active_child == self.child[num] and - self.active_child.wibox.visible)))) - if can_invoke_action then - local visible = action(self.child[num], item) - if not visible then - menu.get_root(self):hide() - return - else - self.child[num]:update() - end - end - if self.active_child and self.active_child ~= self.child[num] then - self.active_child:hide() - end - self.active_child = self.child[num] - if not self.active_child.wibox.visible then - self.active_child:show() - end - elseif type(cmd) == "string" then - menu.get_root(self):hide() - spawn(cmd) - elseif type(cmd) == "function" then - local visible, action = cmd(item, self) - if not visible then - menu.get_root(self):hide() - else - self:update() - if self.items[num] then - self:item_enter(num, opts) - end - end - if action and type(action) == "function" then - action() - end - end -end - -function menu:item_enter(num, opts) - opts = opts or {} - local item = self.items[num] - if num == nil or self.sel == num or not item then - return - elseif self.sel then - self:item_leave(self.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) - self.sel = num - - if self.auto_expand and opts.hover then - if self.active_child then - self.active_child:hide() - self.active_child = nil - end - - if type(item.cmd) == "table" then - self:exec(num, opts) - end - end -end - - -function menu:item_leave(num) - --print("leave", num) - local item = self.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 args The arguments --- @param args.coords Menu position defaulting to mouse.coords() -function menu:show(args) - args = args or {} - local coords = args.coords or nil - local s = capi.screen[screen.focused()] - - if not set_size(self) then return end - set_coords(self, s, coords) - - keygrabber.run(self._keygrabber) - self.wibox.visible = true -end - ---- Hide a menu popup. -function menu:hide() - -- Remove items from screen - for i = 1, #self.items do - self:item_leave(i) - end - if self.active_child then - self.active_child:hide() - self.active_child = nil - end - self.sel = nil - - keygrabber.stop(self._keygrabber) - self.wibox.visible = false -end - ---- Toggle menu visibility. --- @param args The arguments --- @param args.coords Menu position {x,y} -function menu:toggle(args) - if self.wibox.visible then - self:hide() - else - self:show(args) - end -end - ---- Update menu content -function menu:update() - if self.wibox.visible then - self:show({ coords = { x = self.x, y = self.y } }) - end -end - - ---- Get the elder parent so for example when you kill --- it, it will destroy the whole family. -function menu:get_root() - return self.parent and menu.get_root(self.parent) or self -end - ---- Add a new menu entry. --- args.* params needed for the menu entry constructor. --- @param args The item params --- @param args.new (Default: awful.menu.entry) The menu entry constructor. --- @param[opt] args.theme The menu entry theme. --- @param[opt] index The index where the new entry will inserted. -function menu:add(args, index) - if not args then return end - local theme = load_theme(args.theme or {}, self.theme) - args.theme = theme - args.new = args.new or menu.entry - local item = protected_call(args.new, self, args) - if (not item) or (not item.widget) then - print("Error while checking menu entry: no property widget found.") - return - end - item.parent = self - 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.container.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 () self:hide() end), - button({}, 1, function () - local num = util.table.hasitem(self.items, item) - self:item_enter(num, { mouse = true }) - self:exec(num, { exec = true, mouse = true }) - end ))) - - - item._mouse = function () - local num = util.table.hasitem(self.items, item) - self:item_enter(num, { hover = true, moue = true }) - end - item.widget:connect_signal("mouse::enter", item._mouse) - - if index then - self.layout:reset() - table.insert(self.items, index, item) - for _, i in ipairs(self.items) do - self.layout:add(i._background) - end - else - table.insert(self.items, item) - self.layout:add(item._background) - end - if self.wibox then - set_size(self) - end - return item -end - ---- Delete menu entry at given position --- @param num The position in the table of the menu entry to be deleted; can be also the menu entry itself -function menu:delete(num) - if type(num) == "table" then - num = util.table.hasitem(self.items, num) - end - local item = self.items[num] - if not item then return end - item.widget:disconnect_signal("mouse::enter", item._mouse) - item.widget:set_visible(false) - table.remove(self.items, num) - if self.sel == num then - self:item_leave(self.sel) - self.sel = nil - end - self.layout:reset() - for _, i in ipairs(self.items) do - self.layout:add(i._background) - end - if self.child[num] then - self.child[num]:hide() - if self.active_child == self.child[num] then - self.active_child = nil - end - table.remove(self.child, num) - end - if self.wibox then - set_size(self) - end -end - --------------------------------------------------------------------------------- - ---- Build a popup menu with running clients and show it. --- @tparam[opt] table args Menu table, see `new()` for more information. --- @tparam[opt] table item_args Table that will be merged into each item, see --- `new()` for more information. --- @tparam[opt] func filter A function taking a client as an argument and --- returning `true` or `false` to indicate whether the client should be --- included in the menu. --- @return The menu. -function menu.clients(args, item_args, filter) - local cls_t = {} - for c in client_iterate(filter or function() return true end) do - cls_t[#cls_t + 1] = { - c.name or "", - function () - if not c:isvisible() then - tags.viewmore(c:tags(), c.screen) - end - c:emit_signal("request::activate", "menu.clients", {raise=true}) - end, - c.icon } - if item_args then - if type(item_args) == "function" then - util.table.merge(cls_t[#cls_t], item_args(c)) - else - util.table.merge(cls_t[#cls_t], item_args) - end - end - end - args = args or {} - args.items = args.items or {} - util.table.merge(args.items, cls_t) - - local m = menu.new(args) - m:show(args) - return m -end - --------------------------------------------------------------------------------- - ---- Default awful.menu.entry constructor --- @param parent The parent menu (TODO: This is apparently unused) --- @param args the item params --- @return table with 'widget', 'cmd', 'akey' and all the properties the user wants to change -function menu.entry(parent, args) -- luacheck: no unused 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_font(args.theme.font) - 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.container.margin() - margin:set_widget(label) - if args.icon then - icon = surface.load(args.icon) - 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 = cairo.ImageSurface(cairo.Format.ARGB32, w, h) - local cr = cairo.Context(img) - cr:scale(w / iw, h / ih) - cr:set_source_surface(icon, 0, 0) - cr:paint() - icon = img - end - iconbox = wibox.widget.imagebox() - if iconbox:set_image(icon) then - margin:set_left(dpi(2)) - else - iconbox = nil - end - end - if not iconbox then - margin:set_left(args.theme.height + dpi(2)) - end - -- Create the submenu icon widget - local submenu - if type(args.cmd) == "table" then - if args.theme.submenu_icon then - submenu = wibox.widget.imagebox() - submenu:set_image(args.theme.submenu_icon) - else - submenu = wibox.widget.textbox() - submenu:set_font(args.theme.font) - submenu:set_text(args.theme.submenu) - 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_left(left) - if submenu then - layout:set_right(submenu) - end - - return table_update(ret, { - label = label, - sep = submenu, - icon = iconbox, - widget = layout, - cmd = args.cmd, - akey = key, - }) -end - --------------------------------------------------------------------------------- - ---- Create a menu popup. --- @param args Table containing the menu informations. --- --- * 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). --- * 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. --- * Key auto_expand controls the submenu auto expand behaviour by setting it to true (default) or false. --- --- @param parent Specify the parent menu 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 be used to select from dozens of terminals open on --- -- several tags. --- -- When using @{rules.match_any} instead of @{rules.match}, --- -- a menu of clients with different classes could be build. --- --- function terminal_menu () --- terms = {} --- for i, c in pairs(client.get()) do --- if awful.rules.match(c, {class = "URxvt"}) then --- terms[i] = --- {c.name, --- function() --- c.first_tag:view_only() --- client.focus = c --- end, --- c.icon --- } --- end --- end --- awful.menu(terms):show() --- end -function menu.new(args, parent) - args = args or {} - args.layout = args.layout or wibox.layout.flex.vertical - local _menu = table_update(object(), { - item_enter = menu.item_enter, - item_leave = menu.item_leave, - get_root = menu.get_root, - delete = menu.delete, - update = menu.update, - toggle = menu.toggle, - hide = menu.hide, - show = menu.show, - exec = menu.exec, - add = menu.add, - child = {}, - items = {}, - parent = parent, - layout = args.layout(), - theme = load_theme(args.theme or {}, parent and parent.theme) }) - - if parent then - _menu.auto_expand = parent.auto_expand - elseif args.auto_expand ~= nil then - _menu.auto_expand = args.auto_expand - else - _menu.auto_expand = true - end - - -- Create items - for _, v in ipairs(args) do _menu:add(v) end - if args.items then - for _, v in pairs(args.items) do _menu:add(v) end - end - - _menu._keygrabber = function (...) - grabber(_menu, ...) - 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) - - _menu.x = _menu.wibox.x - _menu.y = _menu.wibox.y - return _menu -end - -function menu.mt:__call(...) - return menu.new(...) -end - -return setmetatable(menu, menu.mt) - --- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 |