--[[ Licensed under GNU General Public License v2 * (c) 2016, Luke Bonham * (c) 2015, unknown --]] local awful = require("awful") local capi = { client = client } local math = { floor = math.floor } local string = { format = string.format } local pairs = pairs local screen = screen local setmetatable = setmetatable -- Quake-like Dropdown application spawn local quake = {} -- If you have a rule like "awful.client.setslave" for your terminals, -- ensure you use an exception for QuakeDD. Otherwise, you may -- run into problems with focus. function quake:display() if self.followtag then self.screen = awful.screen.focused() end -- First, we locate the client local client = nil local i = 0 for c in awful.client.iterate(function (c) -- c.name may be changed! return c.instance == self.name end, nil, self.screen) do i = i + 1 if i == 1 then client = c else -- Additional matching clients, let's remove the sticky bit -- which may persist between awesome restarts. We don't close -- them as they may be valuable. They will just turn into -- normal clients. c.sticky = false c.ontop = false c.above = false end end if not client and not self.visible then return end if not client then -- The client does not exist, we spawn it cmd = string.format("%s %s %s", self.app, string.format(self.argname, self.name), self.extra) awful.spawn(cmd, { tag = self.screen.selected_tag }) return end -- Set geometry client.floating = true client.border_width = self.border client.size_hints_honor = false client:geometry(self:compute_size()) -- Set not sticky and on top client.sticky = false client.ontop = true client.above = true client.skip_taskbar = true -- Toggle display if self.visible then client.hidden = false client:raise() self.last_tag = self.screen.selected_tag client:tags({self.screen.selected_tag}) capi.client.focus = client else client.hidden = true local ctags = client:tags() for i, t in pairs(ctags) do ctags[i] = nil end client:tags(ctags) end return client end function quake:compute_size() -- skip if we already have a geometry for this screen if not self.geometry[self.screen] then local geom if not self.overlap then geom = screen[self.screen].workarea else geom = screen[self.screen].geometry end local width, height = self.width, self.height if width <= 1 then width = math.floor(geom.width * width) - 2 * self.border end if height <= 1 then height = math.floor(geom.height * height) end local x, y if self.horiz == "left" then x = geom.x elseif self.horiz == "right" then x = geom.width + geom.x - width else x = geom.x + (geom.width - width)/2 end if self.vert == "top" then y = geom.y elseif self.vert == "bottom" then y = geom.height + geom.y - height else y = geom.y + (geom.height - height)/2 end self.geometry[self.screen] = { x = x, y = y, width = width, height = height } end return self.geometry[self.screen] end function quake:new(config) local conf = config or {} conf.app = conf.app or "xterm" -- application to spawn conf.name = conf.name or "QuakeDD" -- window name conf.argname = conf.argname or "-name %s" -- how to specify window name conf.extra = conf.extra or "" -- extra arguments conf.border = conf.border or 1 -- client border width conf.visible = conf.visible or false -- initially not visible conf.followtag = conf.followtag or false -- spawn on currently focused screen conf.overlap = conf.overlap or false -- overlap wibox conf.screen = conf.screen or awful.screen.focused() -- If width or height <= 1 this is a proportion of the workspace conf.height = conf.height or 0.25 -- height conf.width = conf.width or 1 -- width conf.vert = conf.vert or "top" -- top, bottom or center conf.horiz = conf.horiz or "left" -- left, right or center conf.geometry = {} -- internal use local dropdown = setmetatable(conf, { __index = quake }) capi.client.connect_signal("manage", function(c) if c.instance == dropdown.name and c.screen == dropdown.screen then dropdown:display() end end) capi.client.connect_signal("unmanage", function(c) if c.instance == dropdown.name and c.screen == dropdown.screen then dropdown.visible = false end end) return dropdown end function quake:toggle() if self.followtag then self.screen = awful.screen.focused() end local current_tag = self.screen.selected_tag if current_tag and self.last_tag ~= current_tag and self.visible then self:display():move_to_tag(current_tag) else self.visible = not self.visible self:display() end end return setmetatable(quake, { __call = function(_, ...) return quake:new(...) end })