diff --git a/awesomerc.lua b/awesomerc.lua index 69f78132..6934d522 100755 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -141,12 +141,20 @@ mypromptbox = {} mylayoutbox = {} mytaglist = {} mytaglist.buttons = awful.util.table.join( - awful.button({ }, 1, awful.tag.viewonly), - awful.button({ modkey }, 1, awful.client.movetotag), + awful.button({ }, 1, function(t) t:view_only() end), + awful.button({ modkey }, 1, function(t) + if client.focus then + client.focus:move_to_tag(t) + end + end), awful.button({ }, 3, awful.tag.viewtoggle), - awful.button({ modkey }, 3, awful.client.toggletag), - awful.button({ }, 4, function(t) awful.tag.viewnext(awful.tag.getscreen(t)) end), - awful.button({ }, 5, function(t) awful.tag.viewprev(awful.tag.getscreen(t)) end) + awful.button({ modkey }, 3, function(t) + if client.focus then + client.focus:toggle_tag(t) + end + end), + awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end), + awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end) ) mytasklist = {} @@ -158,8 +166,8 @@ mytasklist.buttons = awful.util.table.join( -- Without this, the following -- :isvisible() makes no sense c.minimized = false - if not c:isvisible() then - awful.tag.viewonly(c.first_tag) + if not c:isvisible() and c.first_tag then + c.first_tag:view_only() end -- This will also un-minimize -- the client, if needed @@ -335,7 +343,7 @@ clientkeys = awful.util.table.join( {description = "toggle floating", group = "client"}), awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end, {description = "move to master", group = "client"}), - awful.key({ modkey, }, "o", awful.client.movetoscreen , + awful.key({ modkey, }, "o", function (c) c:move_to_screen() end, {description = "move to screen", group = "client"}), awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end, {description = "toggle keep on top", group = "client"}), @@ -363,9 +371,9 @@ for i = 1, 9 do awful.key({ modkey }, "#" .. i + 9, function () local screen = awful.screen.focused() - local tag = awful.tag.gettags(screen)[i] + local tag = screen.tags[i] if tag then - awful.tag.viewonly(tag) + tag:view_only() end end, {description = "view tag #"..i, group = "tag"}), @@ -373,7 +381,7 @@ for i = 1, 9 do awful.key({ modkey, "Control" }, "#" .. i + 9, function () local screen = awful.screen.focused() - local tag = awful.tag.gettags(screen)[i] + local tag = screen.tags[i] if tag then awful.tag.viewtoggle(tag) end @@ -383,9 +391,9 @@ for i = 1, 9 do awful.key({ modkey, "Shift" }, "#" .. i + 9, function () if client.focus then - local tag = awful.tag.gettags(client.focus.screen)[i] + local tag = client.focus.screen.tags[i] if tag then - awful.client.movetotag(tag) + client.focus:move_to_tag(tag) end end end, @@ -394,9 +402,9 @@ for i = 1, 9 do awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9, function () if client.focus then - local tag = awful.tag.gettags(client.focus.screen)[i] + local tag = client.focus.screen.tags[i] if tag then - awful.client.toggletag(tag) + client.focus:toggle_tag(tag) end end end, diff --git a/docs/aliases/awful_client.lua b/docs/aliases/awful_client.lua new file mode 100644 index 00000000..b8cd3b80 --- /dev/null +++ b/docs/aliases/awful_client.lua @@ -0,0 +1,6 @@ +--------------------------------------------------------------------------- +--- This module is deprecated, use `client` +-- =============================== +-- +-- @module awful.client +--------------------------------------------------------------------------- diff --git a/docs/aliases/awful_screen.lua b/docs/aliases/awful_screen.lua new file mode 100644 index 00000000..55df367a --- /dev/null +++ b/docs/aliases/awful_screen.lua @@ -0,0 +1,6 @@ +--------------------------------------------------------------------------- +--- This module is deprecated, use `screen` +-- =============================== +-- +-- @module awful.screen +--------------------------------------------------------------------------- diff --git a/docs/aliases/awful_tag.lua b/docs/aliases/awful_tag.lua new file mode 100644 index 00000000..75e3bd2c --- /dev/null +++ b/docs/aliases/awful_tag.lua @@ -0,0 +1,6 @@ +--------------------------------------------------------------------------- +--- This module is deprecated, use `tag` +-- =============================== +-- +-- @module awful.tag +--------------------------------------------------------------------------- diff --git a/docs/config.ld b/docs/config.ld index 994a6473..bec83fd4 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -28,8 +28,19 @@ tparam_alias('client', 'client.object') tparam_alias('tag', 'tag') -- Should be default, but is not. Sets up "@tab" => "@tparam table". tparam_alias('tab', 'table') + +-- Hack to get the functions and method on top of the signals and properties +new_type("function", "Functions") +-- Documentation for objects properties +new_type("property", "Object properties", false, "Type") -- New type for signals new_type("signal", "Signals", false, "Arguments") +-- Allow objects to define a set of beautiful properties affecting them +new_type("beautiful", "Theme variables", false, "Type") +-- Put deprecated methods in their own section +new_type("deprecated", "Deprecated functions", false, "param") +-- For the legacy stateless layout related functions +new_type("legacylayout", "Layout related functions", false, "param") -- More fitting section names kind_names={topic='Documentation', module='Libraries'} @@ -52,6 +63,10 @@ file = { '../objects/', -- LUA libraries '../lib/', + -- Old APIs the user should not longer use directly + '../../docs/aliases/awful_client.lua', + '../../docs/aliases/awful_screen.lua', + '../../docs/aliases/awful_tag.lua', exclude = { -- exclude these modules, as they do not contain any written -- documentation diff --git a/docs/images/client_geo.svg b/docs/images/client_geo.svg new file mode 100644 index 00000000..34c970a2 --- /dev/null +++ b/docs/images/client_geo.svg @@ -0,0 +1,385 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + Screen + + + + + Top titlebar + + + + Bottom titlebar + + + + Left titlebar + + + + Right titlebar + + Client content + + + + + + + + + Top titlebar + Bottom titlebar + Left titlebar + Right titlebar + Client content + + + + + + + + + Shape bounding outline + Shape clip outline + + + + + + + + Border + diff --git a/docs/images/tag_props.svg b/docs/images/tag_props.svg new file mode 100644 index 00000000..fa4a16a7 --- /dev/null +++ b/docs/images/tag_props.svg @@ -0,0 +1,273 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + Gaps + + Work area + + + + + Screen padding + + Master width factor + + + + + Masters + + + + Screen area + Columns + diff --git a/lib/awful/autofocus.lua b/lib/awful/autofocus.lua index f0e9090a..f1117f15 100644 --- a/lib/awful/autofocus.lua +++ b/lib/awful/autofocus.lua @@ -12,7 +12,6 @@ local client = client local aclient = require("awful.client") -local atag = require("awful.tag") local timer = require("gears.timer") --- Give focus when clients appear/disappear. @@ -39,7 +38,7 @@ end -- -- @param tag A tag object local function check_focus_tag(t) - local s = atag.getscreen(t) + local s = t.screen if not s then return end s = screen[s] check_focus({ screen = s }) diff --git a/lib/awful/client.lua b/lib/awful/client.lua index 41feccde..b6e0ef00 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -4,14 +4,13 @@ -- @author Julien Danjou <julien@danjou.info> -- @copyright 2008 Julien Danjou -- @release @AWESOME_VERSION@ --- @module awful.client +-- @module client --------------------------------------------------------------------------- -- Grab environment we need local util = require("awful.util") local spawn = require("awful.spawn") local object = require("gears.object") -local tag = require("awful.tag") local pairs = pairs local type = type local ipairs = ipairs @@ -41,205 +40,78 @@ do __newindex = error -- Just to be sure in case anything ever does this }) end -local client = {} +local client = {object={}} -- Private data client.data = {} -client.data.focus = {} -client.data.urgent = {} client.data.marked = {} client.data.properties = setmetatable({}, { __mode = 'k' }) client.data.persistent_properties_registered = {} -- keys are names of persistent properties, value always true client.data.persistent_properties_loaded = setmetatable({}, { __mode = 'k' }) -- keys are clients, value always true -- Functions -client.urgent = {} -client.focus = {} -client.focus.history = {} +client.urgent = require("awful.client.urgent") client.swap = {} client.floating = {} client.dockable = {} client.property = {} client.shape = require("awful.client.shape") +client.focus = require("awful.client.focus") --- Jump to the given client. -- Takes care of focussing the screen, the right tag, etc. -- +-- @deprecated awful.client.jumpto +-- @see client.jump_to -- @client c the client to jump to -- @tparam bool|function merge If true then merge tags (select the client's -- first tag additionally) when the client is not visible. -- If it is a function, it will be called with the client and its first -- tag as arguments. function client.jumpto(c, merge) + util.deprecate "Use c:jump_to(merge) instead of awful.client.jumpto" + + client.object.jump_to(c, merge) +end + +--- Jump to the given client. +-- Takes care of focussing the screen, the right tag, etc. +-- +-- @function client.jump_to +-- @tparam bool|function merge If true then merge tags (select the client's +-- first tag additionally) when the client is not visible. +-- If it is a function, it will be called with the client and its first +-- tag as arguments. +function client.object.jump_to(self, merge) local s = get_screen(screen.focused()) -- focus the screen - if s ~= get_screen(c.screen) then - screen.focus(c.screen) + if s ~= get_screen(self.screen) then + screen.focus(self.screen) end - c.minimized = false + self.minimized = false -- Try to make client visible, this also covers e.g. sticky. - if not c:isvisible() then - local t = c.first_tag + if not self:isvisible() then + local t = self.first_tag if merge then if type(merge) == "function" then - merge(c, t) + merge(self, t) elseif t then t.selected = true end elseif t then - tag.viewonly(t) + t:view_only() end end - c:emit_signal("request::activate", "client.jumpto", {raise=true}) -end - ---- Get the first client that got the urgent hint. --- --- @treturn client.object The first urgent client. -function client.urgent.get() - if #client.data.urgent > 0 then - return client.data.urgent[1] - else - -- fallback behaviour: iterate through clients and get the first urgent - local clients = capi.client.get() - for _, cl in pairs(clients) do - if cl.urgent then - return cl - end - end - end -end - ---- Jump to the client that received the urgent hint first. --- --- @tparam bool|function merge If true then merge tags (select the client's --- first tag additionally) when the client is not visible. --- If it is a function, it will be called with the client as argument. -function client.urgent.jumpto(merge) - local c = client.urgent.get() - if c then - client.jumpto(c, merge) - end -end - ---- Adds client to urgent stack. --- --- @client c The client object. --- @param prop The property which is updated. -function client.urgent.add(c, prop) - if type(c) == "client" and prop == "urgent" and c.urgent then - table.insert(client.data.urgent, c) - end -end - ---- Remove client from urgent stack. --- --- @client c The client object. -function client.urgent.delete(c) - for k, cl in ipairs(client.data.urgent) do - if c == cl then - table.remove(client.data.urgent, k) - break - end - end -end - ---- Remove a client from the focus history --- --- @client c The client that must be removed. -function client.focus.history.delete(c) - for k, v in ipairs(client.data.focus) do - if v == c then - table.remove(client.data.focus, k) - break - end - end -end - ---- Filter out window that we do not want handled by focus. --- This usually means that desktop, dock and splash windows are --- not registered and cannot get focus. --- --- @client c A client. --- @return The same client if it's ok, nil otherwise. -function client.focus.filter(c) - if c.type == "desktop" - or c.type == "dock" - or c.type == "splash" - or not c.focusable then - return nil - end - return c -end - ---- Update client focus history. --- --- @client c The client that has been focused. -function client.focus.history.add(c) - -- Remove the client if its in stack - client.focus.history.delete(c) - -- Record the client has latest focused - table.insert(client.data.focus, 1, c) -end - ---- Get the latest focused client for a screen in history. --- --- @tparam int|screen s The screen to look for. --- @tparam int idx The index: 0 will return first candidate, --- 1 will return second, etc. --- @tparam function filter An optional filter. If no client is found in the --- first iteration, client.focus.filter is used by default to get any --- client. --- @treturn client.object A client. -function client.focus.history.get(s, idx, filter) - s = get_screen(s) - -- When this counter is equal to idx, we return the client - local counter = 0 - local vc = client.visible(s, true) - for _, c in ipairs(client.data.focus) do - if get_screen(c.screen) == s then - if not filter or filter(c) then - for _, vcc in ipairs(vc) do - if vcc == c then - if counter == idx then - return c - end - -- We found one, increment the counter only. - counter = counter + 1 - break - end - end - end - end - end - -- Argh nobody found in history, give the first one visible if there is one - -- that passes the filter. - filter = filter or client.focus.filter - if counter == 0 then - for _, v in ipairs(vc) do - if filter(v) then - return v - end - end - end -end - ---- Focus the previous client in history. -function client.focus.history.previous() - local sel = capi.client.focus - local s = sel and sel.screen or screen.focused() - local c = client.focus.history.get(s, 1) - if c then - c:emit_signal("request::activate", "client.focus.history.previous", - {raise=false}) - end + self:emit_signal("request::activate", "client.jumpto", {raise=true}) end --- Get visible clients from a screen. -- +-- @deprecated awful.client.visible +-- @see screen.clients -- @tparam[opt] integer|screen s The screen, or nil for all screens. -- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) -- @treturn table A table with all visible clients. @@ -256,6 +128,8 @@ end --- Get visible and tiled clients -- +-- @deprecated awful.client.tiled +-- @see screen.tiled_clients -- @tparam integer|screen s The screen, or nil for all screens. -- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) -- @treturn table A table with all visible and tiled clients. @@ -264,7 +138,7 @@ function client.tiled(s, stacked) local tclients = {} -- Remove floating clients for _, c in pairs(clients) do - if not client.floating.get(c) + if not client.object.get_floating(c) and not c.fullscreen and not c.maximized_vertical and not c.maximized_horizontal then @@ -277,6 +151,7 @@ end --- Get a client by its relative index to another client. -- If no client is passed, the focused client will be used. -- +-- @function awful.client.next -- @tparam int i The index. Use 1 to get the next, -1 to get the previous. -- @client[opt] sel The client. -- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) @@ -310,76 +185,8 @@ function client.next(i, sel, stacked) end end ---- Focus a client by the given direction. --- --- @tparam string dir The direction, can be either --- `"up"`, `"down"`, `"left"` or `"right"`. --- @client[opt] c The client. --- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) -function client.focus.bydirection(dir, c, stacked) - local sel = c or capi.client.focus - if sel then - local cltbl = client.visible(sel.screen, stacked) - local geomtbl = {} - for i,cl in ipairs(cltbl) do - geomtbl[i] = cl:geometry() - end - - local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) - - -- If we found a client to focus, then do it. - if target then - cltbl[target]:emit_signal("request::activate", - "client.focus.bydirection", {raise=false}) - end - end -end - ---- Focus a client by the given direction. Moves across screens. --- --- @param dir The direction, can be either "up", "down", "left" or "right". --- @client[opt] c The client. --- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) -function client.focus.global_bydirection(dir, c, stacked) - local sel = c or capi.client.focus - local scr = get_screen(sel and sel.screen or screen.focused()) - - -- change focus inside the screen - client.focus.bydirection(dir, sel) - - -- if focus not changed, we must change screen - if sel == capi.client.focus then - screen.focus_bydirection(dir, scr) - if scr ~= get_screen(screen.focused()) then - local cltbl = client.visible(screen.focused(), stacked) - local geomtbl = {} - for i,cl in ipairs(cltbl) do - geomtbl[i] = cl:geometry() - end - local target = util.get_rectangle_in_direction(dir, geomtbl, scr.geometry) - - if target then - cltbl[target]:emit_signal("request::activate", - "client.focus.global_bydirection", - {raise=false}) - end - end - end -end - ---- Focus a client by its relative index. --- --- @param i The index. --- @client[opt] c The client. -function client.focus.byidx(i, c) - local target = client.next(i, c) - if target then - target:emit_signal("request::activate", "client.focus.byidx", - {raise=true}) - end -end - --- Swap a client with another client in the given direction. +-- @function awful.client.swap.bydirection -- @tparam string dir The direction, can be either "up", "down", "left" or "right". -- @client[opt=focused] c The client. -- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) @@ -401,6 +208,7 @@ function client.swap.bydirection(dir, c, stacked) end --- Swap a client with another client in the given direction. Swaps across screens. +-- @function awful.client.swap.global_bydirection -- @param dir The direction, can be either "up", "down", "left" or "right". -- @client[opt] sel The client. function client.swap.global_bydirection(dir, sel) @@ -418,12 +226,12 @@ function client.swap.global_bydirection(dir, sel) -- swapping to an empty screen elseif get_screen(sel.screen) ~= get_screen(c.screen) and sel == c then - client.movetoscreen(sel, screen.focused()) + sel:move_to_screen(screen.focused()) -- swapping to a nonempty screen elseif get_screen(sel.screen) ~= get_screen(c.screen) and sel ~= c then - client.movetoscreen(sel, c.screen) - client.movetoscreen(c, scr) + sel:move_to_screen(c.screen) + c:move_to_screen(scr) end screen.focus(sel.screen) @@ -433,6 +241,7 @@ function client.swap.global_bydirection(dir, sel) end --- Swap a client by its relative index. +-- @function awful.client.swap.byidx -- @param i The index. -- @client[opt] c The client, otherwise focused one is used. function client.swap.byidx(i, c) @@ -445,6 +254,7 @@ end --- Cycle clients. -- +-- @function awful.client.cycle -- @param clockwise True to cycle clients clockwise. -- @param[opt] s The screen where to cycle clients. -- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) @@ -468,6 +278,7 @@ end --- Get the master window. -- +-- @legacylayout awful.client.getmaster -- @param[opt] s The screen number, defaults to focused screen. -- @return The master window. function client.getmaster(s) @@ -477,6 +288,7 @@ end --- Set the client as master: put it at the beginning of other windows. -- +-- @legacylayout awful.client.setmaster -- @client c The window to set as master. function client.setmaster(c) local cls = util.table.reverse(capi.client.get(c.screen)) @@ -486,6 +298,7 @@ function client.setmaster(c) end --- Set the client as slave: put it at the end of other windows. +-- @legacylayout awful.client.setslave -- @client c The window to set as slave. function client.setslave(c) local cls = capi.client.get(c.screen) @@ -495,45 +308,76 @@ function client.setslave(c) end --- Move/resize a client relative to current coordinates. +-- @deprecated awful.client.moveresize -- @param x The relative x coordinate. -- @param y The relative y coordinate. -- @param w The relative width. -- @param h The relative height. -- @client[opt] c The client, otherwise focused one is used. +-- @see client.relative_move function client.moveresize(x, y, w, h, c) - local sel = c or capi.client.focus - local geometry = sel:geometry() + util.deprecate "Use c:relative_move(x, y, w, h) instead of awful.client.moveresize" + client.object.relative_move(c or capi.client.focus, x, y, w, h) +end + +--- Move/resize a client relative to current coordinates. +-- @function client.relative_move +-- @see geometry +-- @tparam[opt=c.x] number x The relative x coordinate. +-- @tparam[opt=c.y] number y The relative y coordinate. +-- @tparam[opt=c.width] number w The relative width. +-- @tparam[opt=c.height] number h The relative height. +function client.object.relative_move(self, x, y, w, h) + local geometry = self:geometry() geometry['x'] = geometry['x'] + x geometry['y'] = geometry['y'] + y geometry['width'] = geometry['width'] + w geometry['height'] = geometry['height'] + h - sel:geometry(geometry) + self:geometry(geometry) end --- Move a client to a tag. +-- @deprecated awful.client.movetotag -- @param target The tag to move the client to. -- @client[opt] c The client to move, otherwise the focused one is used. +-- @see client.move_to_tag function client.movetotag(target, c) - local sel = c or capi.client.focus - local s = tag.getscreen(target) - if sel and s then - if sel == capi.client.focus then - sel:emit_signal("request::activate", "client.movetotag", {raise=true}) + util.deprecate "Use c:move_to_tag(target) instead of awful.client.movetotag" + client.object.move_to_tag(c or capi.client.focus, target) +end + +--- Move a client to a tag. +-- @function client.move_to_tag +-- @tparam tag target The tag to move the client to. +function client.object.move_to_tag(self, target) + local s = target.screen + if self and s then + if self == capi.client.focus then + self:emit_signal("request::activate", "client.movetotag", {raise=true}) end -- Set client on the same screen as the tag. - sel.screen = s - sel:tags({ target }) + self.screen = s + self:tags({ target }) end end --- Toggle a tag on a client. +-- @deprecated awful.client.toggletag -- @param target The tag to toggle. -- @client[opt] c The client to toggle, otherwise the focused one is used. +-- @see client.toggle_tag function client.toggletag(target, c) - local sel = c or capi.client.focus + util.deprecate "Use c:toggle_tag(target) instead of awful.client.toggletag" + client.object.toggle_tag(c or capi.client.focus, target) +end + +--- Toggle a tag on a client. +-- @function client.toggle_tag +-- @tparam tag target The tag to move the client to. +function client.object.toggle_tag(self, target) -- Check that tag and client screen are identical - if sel and get_screen(sel.screen) == get_screen(tag.getscreen(target)) then - local tags = sel:tags() + if self and get_screen(self.screen) == get_screen(target.screen) then + local tags = self:tags() local index = nil; for i, v in ipairs(tags) do if v == target then @@ -548,114 +392,184 @@ function client.toggletag(target, c) else tags[#tags + 1] = target end - sel:tags(tags) + self:tags(tags) end end --- Move a client to a screen. Default is next screen, cycling. +-- @deprecated awful.client.movetoscreen -- @client c The client to move. -- @param s The screen, default to current + 1. +-- @see screen +-- @see client.move_to_screen function client.movetoscreen(c, s) - local sel = c or capi.client.focus - if sel then + util.deprecate "Use c:move_to_screen(s) instead of awful.client.movetoscreen" + + client.object.move_to_screen(c or capi.client.focus, s) +end + +--- Move a client to a screen. Default is next screen, cycling. +-- @function client.move_to_screen +-- @tparam[opt=c.screen.index+1] screen s The screen, default to current + 1. +-- @see screen +-- @see request::activate +function client.object.move_to_screen(self, s) + if self then local sc = capi.screen.count() if not s then - s = sel.screen.index + 1 + s = self.screen.index + 1 end if type(s) == "number" then if s > sc then s = 1 elseif s < 1 then s = sc end end s = get_screen(s) - if get_screen(sel.screen) ~= s then - local sel_is_focused = sel == capi.client.focus - sel.screen = s + if get_screen(self.screen) ~= s then + local sel_is_focused = self == capi.client.focus + self.screen = s screen.focus(s) if sel_is_focused then - sel:emit_signal("request::activate", "client.movetoscreen", + self:emit_signal("request::activate", "client.movetoscreen", {raise=true}) end end end end ---- Mark a client, and then call 'marked' hook. --- @client c The client to mark, the focused one if not specified. --- @return True if the client has been marked. False if the client was already marked. -function client.mark(c) - local cl = c or capi.client.focus - if cl then - for _, v in pairs(client.data.marked) do - if cl == v then - return false +--- Tag a client with the set of current tags. +-- @function client.to_selected_tags +-- @see screen.selected_tags +function client.object.to_selected_tags(self) + local tags = {} + + for _, t in ipairs(self:tags()) do + if get_screen(t.screen) == get_screen(self.screen) then + table.insert(tags, t) + end + end + + if #tags == 0 then + tags = self.screen.selected_tags + end + + if #tags == 0 then + tags = self.screen.tags + end + + if #tags ~= 0 then + self:tags(tags) + end +end + +--- If a client is marked or not. +-- +-- **Signal:** +-- +-- * *marked* (for legacy reasons, use `property::marked`) +-- * *unmarked* (for legacy reasons, use `property::marked`) +-- * *property::marked* +-- +-- @property marked +-- @param boolean + +--- The border color when the client is focused. +-- +-- @beautiful beautiful.border_marked +-- @param string +-- + +function client.object.set_marked(self, value) + local is_marked = self.marked + + if value == false and is_marked then + for k, v in pairs(client.data.marked) do + if self == v then + table.remove(client.data.marked, k) end end - - table.insert(client.data.marked, cl) - - -- Call callback - cl:emit_signal("marked") - return true + self:emit_signal("unmarked") + elseif not is_marked and value then + self:emit_signal("marked") + table.insert(client.data.marked, self) end + + client.property.set(self, "marked", value) +end + +function client.object.get_marked(self) + return client.property.get(self, "marked") +end + +--- Mark a client, and then call 'marked' hook. +-- @deprecated awful.client.mark +-- @client c The client to mark, the focused one if not specified. +function client.mark(c) + util.deprecate "Use c.marked = true instead of awful.client.mark" + + client.object.set_marked(c or capi.client.focus, true) end --- Unmark a client and then call 'unmarked' hook. +-- @deprecated awful.client.unmark -- @client c The client to unmark, or the focused one if not specified. --- @return True if the client has been unmarked. False if the client was not marked. function client.unmark(c) - local cl = c or capi.client.focus + util.deprecate "Use c.marked = false instead of awful.client.unmark" - for k, v in pairs(client.data.marked) do - if cl == v then - table.remove(client.data.marked, k) - cl:emit_signal("unmarked") - return true - end - end - - return false + client.object.set_marked(c or capi.client.focus, false) end --- Check if a client is marked. +-- @deprecated awful.client.ismarked -- @client c The client to check, or the focused one otherwise. function client.ismarked(c) - local cl = c or capi.client.focus - if cl then - for _, v in pairs(client.data.marked) do - if cl == v then - return true - end - end - end - return false + util.deprecate "Use c.marked instead of awful.client.ismarked" + + return client.object.get_marked(c or capi.client.focus) end --- Toggle a client as marked. +-- @deprecated awful.client.togglemarked -- @client c The client to toggle mark. function client.togglemarked(c) + util.deprecate "Use c.marked = not c.marked instead of awful.client.togglemarked" + c = c or capi.client.focus - if not client.mark(c) then - client.unmark(c) + if c then + c.marked = not c.marked end end --- Return the marked clients and empty the marked table. +-- @function awful.client.getmarked -- @return A table with all marked clients. function client.getmarked() - for _, v in pairs(client.data.marked) do + local copy = util.table.clone(client.data.marked, false) + + for _, v in pairs(copy) do + client.property.set(v, "marked", false) v:emit_signal("unmarked") end - local t = client.data.marked client.data.marked = {} - return t + + return copy end --- Set a client floating state, overriding auto-detection. -- Floating client are not handled by tiling layouts. +-- @deprecated awful.client.floating.set -- @client c A client. -- @param s True or false. function client.floating.set(c, s) + util.deprecate "Use c.floating = true instead of awful.client.floating.set" + client.object.set_floating(c, s) +end + +-- Set a client floating state, overriding auto-detection. +-- Floating client are not handled by tiling layouts. +-- @client c A client. +-- @param s True or false. +function client.object.set_floating(c, s) c = c or capi.client.focus if c and client.property.get(c, "floating") ~= s then client.property.set(c, "floating", s) @@ -668,7 +582,7 @@ function client.floating.set(c, s) end local function store_floating_geometry(c) - if client.floating.get(c) then + if client.object.get_floating(c) then client.property.set(c, "floating_geometry", c:geometry()) end end @@ -684,10 +598,31 @@ end) capi.client.connect_signal("property::geometry", store_floating_geometry) ---- Return if a client has a fixe size or not. +--- Return if a client has a fixed size or not. +-- This function is deprecated, use `c.is_fixed` -- @client c The client. +-- @deprecated awful.client.isfixed +-- @see is_fixed +-- @see size_hints_honor function client.isfixed(c) + util.deprecate "Use c.is_fixed instead of awful.client.isfixed" c = c or capi.client.focus + return client.object.is_fixed(c) +end + +--- Return if a client has a fixed size or not. +-- +-- **Signal:** +-- +-- * *property::is_fixed* +-- +-- This property is read only. +-- @property is_fixed +-- @param boolean The floating state +-- @see size_hints +-- @see size_hints_honor + +function client.object.is_fixed(c) if not c then return end local h = c.size_hints if h.min_width and h.max_width @@ -703,10 +638,31 @@ end --- Get a client floating state. -- @client c A client. +-- @see floating +-- @deprecated awful.client.floating.get -- @return True or false. Note that some windows might be floating even if you -- did not set them manually. For example, windows with a type different than -- normal. function client.floating.get(c) + util.deprecate "Use c.floating instead of awful.client.floating.get" + return client.object.get_floating(c) +end + +--- The client floating state. +-- If the client is part of the tiled layout or free floating. +-- +-- Note that some windows might be floating even if you +-- did not set them manually. For example, windows with a type different than +-- normal. +-- +-- **Signal:** +-- +-- * *property::floating* +-- +-- @property floating +-- @param boolean The floating state + +function client.object.get_floating(c) c = c or capi.client.focus if c then local value = client.property.get(c, "floating") @@ -717,7 +673,7 @@ function client.floating.get(c) or c.fullscreen or c.maximized_vertical or c.maximized_horizontal - or client.isfixed(c) then + or client.object.is_fixed(c) then return true end return false @@ -725,30 +681,30 @@ function client.floating.get(c) end --- Toggle the floating state of a client between 'auto' and 'true'. +-- Use `c.floating = not c.floating` +-- @deprecated awful.client.floating.toggle -- @client c A client. +-- @see floating function client.floating.toggle(c) c = c or capi.client.focus -- If it has been set to floating - if client.floating.get(c) then - client.floating.set(c, false) - else - client.floating.set(c, true) - end + client.object.set_floating(c, not client.object.get_floating(c)) end ---- Remove the floating information on a client. +-- Remove the floating information on a client. -- @client c The client. function client.floating.delete(c) - client.floating.set(c, nil) + client.object.set_floating(c, nil) end --- Restore (=unminimize) a random client. +-- @function awful.client.restore -- @param s The screen to use. -- @return The restored client if some client was restored, otherwise nil. function client.restore(s) s = s or screen.focused() local cls = capi.client.get(s) - local tags = tag.selectedlist(s) + local tags = s.selected_tags for _, c in pairs(cls) do local ctags = c:tags() if c.minimized then @@ -790,6 +746,7 @@ end --- Calculate a client's column number, index in that column, and -- number of visible clients in this column. -- +-- @legacylayout awful.client.idx -- @client c the client -- @return col the column number -- @return idx index of the client in the column @@ -808,8 +765,8 @@ function client.idx(c) end end - local t = tag.selected(c.screen) - local nmaster = tag.getnmaster(t) + local t = c.screen.selected_tag + local nmaster = t.master_count -- This will happen for floating or maximized clients if not idx then return nil end @@ -824,7 +781,7 @@ function client.idx(c) -- based on the how the tiling algorithm places clients we calculate -- the column, we could easily use the for loop in the program but we can -- calculate it. - local ncol = tag.getncol(t) + local ncol = t.column_count -- minimum number of clients per column local percol = math.floor(nother / ncol) -- number of columns with an extra client @@ -850,6 +807,7 @@ end --- Set the window factor of a client -- +-- @legacylayout awful.client.setwfact -- @param wfact the window factor value -- @client c the client function client.setwfact(wfact, c) @@ -861,10 +819,10 @@ function client.setwfact(wfact, c) if not w then return end - local t = tag.selected(c.screen) + local t = c.screen.selected_tag -- n is the number of windows currently visible for which we have to be concerned with the properties - local data = tag.getproperty(t, "windowfact") or {} + local data = t.windowfact or {} local colfact = data[w.col] local need_normalize = colfact ~= nil @@ -902,17 +860,18 @@ end --- Increment a client's window factor -- +-- @legacylayout awful.client.incwfact -- @param add amount to increase the client's window -- @client c the client function client.incwfact(add, c) c = c or capi.client.focus if not c then return end - local t = tag.selected(c.screen) + local t = c.screen.selected_tag local w = client.idx(c) - local data = tag.getproperty(t, "windowfact") or {} + local data = t.windowfact or {} local colfact = data[w.col] or {} local curr = colfact[w.idx] or 1 colfact[w.idx] = curr + add @@ -929,7 +888,25 @@ end -- @return True or false. Note that some windows might be dockable even if you -- did not set them manually. For example, windows with a type "utility", -- "toolbar" or "dock" +-- @deprecated awful.client.dockable.get function client.dockable.get(c) + util.deprecate "Use c.dockable instead of awful.client.dockable.get" + + return client.object.get_dockable(c) +end + +--- If the client is dockable. +-- A dockable client is an application confined to the edge of the screen. The +-- space it occupy is substracted from the `screen.workarea`. +-- +-- **Signal:** +-- +-- * *property::dockable* +-- +-- @property dockable +-- @param boolean The dockable state + +function client.object.get_dockable(c) local value = client.property.get(c, "dockable") -- Some sane defaults @@ -950,15 +927,20 @@ end -- -- @client c A client. -- @param value True or false. +-- @deprecated awful.client.dockable.set function client.dockable.set(c, value) + util.deprecate "Use c.dockable = value instead of awful.client.dockable.set" client.property.set(c, "dockable", value) end --- Get a client property. -- +-- This method is deprecated. It is now possible to use `c.value` directly. +-- -- @client c The client. -- @param prop The property name. -- @return The property. +-- @deprecated awful.client.property.get function client.property.get(c, prop) if not client.data.persistent_properties_loaded[c] then client.data.persistent_properties_loaded[c] = true @@ -975,11 +957,14 @@ function client.property.get(c, prop) end --- Set a client property. --- These properties are internal to awful. Some are used to move clients, etc. +-- +-- This method is deprecated. It is now possible to use `c.value = value` +-- directly. -- -- @client c The client. -- @param prop The property name. -- @param value The value. +-- @deprecated awful.client.property.set function client.property.set(c, prop, value) if not client.data.properties[c] then client.data.properties[c] = {} @@ -995,6 +980,7 @@ end --- Set a client property to be persistent across restarts (via X properties). -- +-- @function awful.client.property.persist -- @param prop The property name. -- @param kind The type (used for register_xproperty). -- One of "string", "number" or "boolean". @@ -1020,6 +1006,7 @@ end -- index of the currently focused client. -- @param s which screen to use. nil means all screens. -- +-- @function awful.client.iterate -- @usage -- un-minimize all urxvt instances -- local urxvt = function (c) -- return awful.rules.match(c, {class = "URxvt"}) @@ -1045,6 +1032,7 @@ end -- first tag additionally) when the client is not visible. -- If it is a function, it will be called with the client as argument. -- +-- @function awful.client.run_or_raise -- @usage -- run or raise urxvt (perhaps, with tabs) on modkey + semicolon -- awful.key({ modkey, }, 'semicolon', function () -- local matcher = function (c) @@ -1067,12 +1055,26 @@ function client.run_or_raise(cmd, matcher, merge) end --- Get a matching transient_for client (if any). +-- @deprecated awful.client.get_transient_for_matching +-- @see client.get_transient_for_matching -- @client c The client. -- @tparam function matcher A function that should return true, if -- a matching parent client is found. -- @treturn client.client|nil The matching parent client or nil. function client.get_transient_for_matching(c, matcher) - local tc = c.transient_for + util.deprecate ("Use c:get_transient_for_matching(matcher) instead of".. + "awful.client.get_transient_for_matching") + + return client.object.get_transient_for_matching(c, matcher) +end + +--- Get a matching transient_for client (if any). +-- @function client.get_transient_for_matching +-- @tparam function matcher A function that should return true, if +-- a matching parent client is found. +-- @treturn client.client|nil The matching parent client or nil. +function client.object.get_transient_for_matching(self, matcher) + local tc = self.transient_for while tc do if matcher(tc) then return tc @@ -1083,11 +1085,24 @@ function client.get_transient_for_matching(c, matcher) end --- Is a client transient for another one? +-- @deprecated awful.client.is_transient_for +-- @see client.is_transient_for -- @client c The child client (having transient_for). -- @client c2 The parent client to check. -- @treturn client.client|nil The parent client or nil. function client.is_transient_for(c, c2) - local tc = c + util.deprecate ("Use c:is_transient_for(c2) instead of".. + "awful.client.is_transient_for") + + return client.object.is_transient_for(c, c2) +end + +--- Is a client transient for another one? +-- @function client.is_transient_for +-- @client c2 The parent client to check. +-- @treturn client.client|nil The parent client or nil. +function client.object.is_transient_for(self, c2) + local tc = self while tc.transient_for do if tc.transient_for == c2 then return tc @@ -1098,10 +1113,21 @@ function client.is_transient_for(c, c2) end -- Register standards signals + +--- The last geometry when client was floating. +-- @signal property::floating_geometry capi.client.add_signal("property::floating_geometry") + capi.client.add_signal("property::floating") + capi.client.add_signal("property::dockable") + +--- The client marked signal (deprecated). +-- @signal .marked capi.client.add_signal("marked") + +--- The client unmarked signal (deprecated). +-- @signal unmarked capi.client.add_signal("unmarked") capi.client.connect_signal("focus", client.focus.history.add) @@ -1116,9 +1142,6 @@ capi.client.connect_signal("manage", function (c) end) capi.client.connect_signal("unmanage", client.focus.history.delete) -capi.client.connect_signal("property::urgent", client.urgent.add) -capi.client.connect_signal("focus", client.urgent.delete) -capi.client.connect_signal("unmanage", client.urgent.delete) capi.client.connect_signal("unmanage", client.floating.delete) @@ -1127,8 +1150,8 @@ client.property.persist("floating", "boolean") -- Extend the luaobject object.properties(capi.client, { - getter_class = client, - setter_class = client, + getter_class = client.object, + setter_class = client.object, getter_fallback = client.property.get, setter_fallback = client.property.set, }) diff --git a/lib/awful/client/focus.lua b/lib/awful/client/focus.lua new file mode 100644 index 00000000..1b826b1e --- /dev/null +++ b/lib/awful/client/focus.lua @@ -0,0 +1,218 @@ +--------------------------------------------------------------------------- +--- Keep track of the focused clients. +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008 Julien Danjou +-- @release @AWESOME_VERSION@ +-- @submodule client +--------------------------------------------------------------------------- +local util = require("awful.util") + +local capi = +{ + screen = screen, + client = client, +} + +-- We use a metatable to prevent circular dependency loops. +local screen +do + screen = setmetatable({}, { + __index = function(_, k) + screen = require("awful.screen") + return screen[k] + end, + __newindex = error -- Just to be sure in case anything ever does this + }) +end + +local client +do + client = setmetatable({}, { + __index = function(_, k) + client = require("awful.client") + return client[k] + end, + __newindex = error -- Just to be sure in case anything ever does this + }) +end + +local focus = {history = {}} + +local internal = {} + +local function get_screen(s) + return s and capi.screen[s] +end + +--- Remove a client from the focus history +-- +-- @client c The client that must be removed. +-- @function awful.client.focus.history.delete +function focus.history.delete(c) + for k, v in ipairs(internal) do + if v == c then + table.remove(internal, k) + break + end + end +end + +--- Focus a client by its relative index. +-- +-- @function awful.client.focus.byidx +-- @param i The index. +-- @client[opt] c The client. +function focus.byidx(i, c) + local target = client.next(i, c) + if target then + target:emit_signal("request::activate", "client.focus.byidx", + {raise=true}) + end +end + +--- Filter out window that we do not want handled by focus. +-- This usually means that desktop, dock and splash windows are +-- not registered and cannot get focus. +-- +-- @client c A client. +-- @return The same client if it's ok, nil otherwise. +-- @function awful.client.focus.filter +function focus.filter(c) + if c.type == "desktop" + or c.type == "dock" + or c.type == "splash" + or not c.focusable then + return nil + end + return c +end + +--- Update client focus history. +-- +-- @client c The client that has been focused. +-- @function awful.client.focus.history.add +function focus.history.add(c) + -- Remove the client if its in stack + focus.history.delete(c) + -- Record the client has latest focused + table.insert(internal, 1, c) +end + +--- Get the latest focused client for a screen in history. +-- +-- @tparam int|screen s The screen to look for. +-- @tparam int idx The index: 0 will return first candidate, +-- 1 will return second, etc. +-- @tparam function filter An optional filter. If no client is found in the +-- first iteration, `awful.client.focus.filter` is used by default to get any +-- client. +-- @treturn client.object A client. +-- @function awful.client.focus.history.get +function focus.history.get(s, idx, filter) + s = get_screen(s) + -- When this counter is equal to idx, we return the client + local counter = 0 + local vc = client.visible(s, true) + for _, c in ipairs(internal) do + if get_screen(c.screen) == s then + if not filter or filter(c) then + for _, vcc in ipairs(vc) do + if vcc == c then + if counter == idx then + return c + end + -- We found one, increment the counter only. + counter = counter + 1 + break + end + end + end + end + end + -- Argh nobody found in history, give the first one visible if there is one + -- that passes the filter. + filter = filter or focus.filter + if counter == 0 then + for _, v in ipairs(vc) do + if filter(v) then + return v + end + end + end +end + +--- Focus the previous client in history. +-- @function awful.client.focus.history.previous +function focus.history.previous() + local sel = capi.client.focus + local s = sel and sel.screen or screen.focused() + local c = focus.history.get(s, 1) + if c then + c:emit_signal("request::activate", "client.focus.history.previous", + {raise=false}) + end +end + +--- Focus a client by the given direction. +-- +-- @tparam string dir The direction, can be either +-- `"up"`, `"down"`, `"left"` or `"right"`. +-- @client[opt] c The client. +-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) +-- @function awful.client.focus.bydirection +function focus.bydirection(dir, c, stacked) + local sel = c or capi.client.focus + if sel then + local cltbl = client.visible(sel.screen, stacked) + local geomtbl = {} + for i,cl in ipairs(cltbl) do + geomtbl[i] = cl:geometry() + end + + local target = util.get_rectangle_in_direction(dir, geomtbl, sel:geometry()) + + -- If we found a client to focus, then do it. + if target then + cltbl[target]:emit_signal("request::activate", + "client.focus.bydirection", {raise=false}) + end + end +end + +--- Focus a client by the given direction. Moves across screens. +-- +-- @param dir The direction, can be either "up", "down", "left" or "right". +-- @client[opt] c The client. +-- @tparam[opt=false] boolean stacked Use stacking order? (top to bottom) +-- @function awful.client.focus.global_bydirection +function focus.global_bydirection(dir, c, stacked) + local sel = c or capi.client.focus + local scr = get_screen(sel and sel.screen or screen.focused()) + + -- change focus inside the screen + focus.bydirection(dir, sel) + + -- if focus not changed, we must change screen + if sel == capi.client.focus then + screen.focus_bydirection(dir, scr) + if scr ~= get_screen(screen.focused()) then + local cltbl = client.visible(screen.focused(), stacked) + local geomtbl = {} + for i,cl in ipairs(cltbl) do + geomtbl[i] = cl:geometry() + end + local target = util.get_rectangle_in_direction(dir, geomtbl, scr.geometry) + + if target then + cltbl[target]:emit_signal("request::activate", + "client.focus.global_bydirection", + {raise=false}) + end + end + end +end + +return focus + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/client/shape.lua b/lib/awful/client/shape.lua index e1280e0e..e21fe238 100644 --- a/lib/awful/client/shape.lua +++ b/lib/awful/client/shape.lua @@ -4,7 +4,7 @@ -- @author Uli Schlachter <psychon@znc.in> -- @copyright 2014 Uli Schlachter -- @release @AWESOME_VERSION@ --- @module awful.client.shape +-- @submodule client --------------------------------------------------------------------------- -- Grab environment we need @@ -18,7 +18,8 @@ local capi = local shape = {} shape.update = {} ---- Get one of a client's shapes and transform it to include window decorations +--- Get one of a client's shapes and transform it to include window decorations. +-- @function awful.shape.get_transformed -- @client c The client whose shape should be retrieved -- @tparam string shape_name Either "bounding" or "clip" function shape.get_transformed(c, shape_name) @@ -51,7 +52,8 @@ function shape.get_transformed(c, shape_name) return result end ---- Update a client's bounding shape from the shape the client set itself +--- Update a client's bounding shape from the shape the client set itself. +-- @function awful.shape.update.bounding -- @client c The client to act on function shape.update.bounding(c) local res = shape.get_transformed(c, "bounding") @@ -62,7 +64,8 @@ function shape.update.bounding(c) end end ---- Update a client's clip shape from the shape the client set itself +--- Update a client's clip shape from the shape the client set itself. +-- @function awful.shape.update.clip -- @client c The client to act on function shape.update.clip(c) local res = shape.get_transformed(c, "clip") @@ -73,7 +76,8 @@ function shape.update.clip(c) end end ---- Update all of a client's shapes from the shapes the client set itself +--- Update all of a client's shapes from the shapes the client set itself. +-- @function awful.shape.update.all -- @client c The client to act on function shape.update.all(c) shape.update.bounding(c) diff --git a/lib/awful/client/urgent.lua b/lib/awful/client/urgent.lua new file mode 100644 index 00000000..051bc2ee --- /dev/null +++ b/lib/awful/client/urgent.lua @@ -0,0 +1,89 @@ +--------------------------------------------------------------------------- +--- Keep track of the urgent clients. +-- +-- @author Julien Danjou <julien@danjou.info> +-- @copyright 2008 Julien Danjou +-- @release @AWESOME_VERSION@ +-- @submodule client +--------------------------------------------------------------------------- + +local urgent = {} + +local capi = +{ + client = client, +} + +local client +do + client = setmetatable({}, { + __index = function(_, k) + client = require("awful.client") + return client[k] + end, + __newindex = error -- Just to be sure in case anything ever does this + }) +end + +local data = setmetatable({}, { __mode = 'k' }) + +--- Get the first client that got the urgent hint. +-- +-- @function awful.urgent.get +-- @treturn client.object The first urgent client. +function urgent.get() + if #data > 0 then + return data[1] + else + -- fallback behaviour: iterate through clients and get the first urgent + local clients = capi.client.get() + for _, cl in pairs(clients) do + if cl.urgent then + return cl + end + end + end +end + +--- Jump to the client that received the urgent hint first. +-- +-- @function awful.urgent.jumpto +-- @tparam bool|function merge If true then merge tags (select the client's +-- first tag additionally) when the client is not visible. +-- If it is a function, it will be called with the client as argument. +function urgent.jumpto(merge) + local c = client.urgent.get() + if c then + client.jumpto(c, merge) + end +end + +--- Adds client to urgent stack. +-- +-- @function awful.urgent.add +-- @client c The client object. +-- @param prop The property which is updated. +function urgent.add(c, prop) + if type(c) == "client" and prop == "urgent" and c.urgent then + table.insert(data, c) + end +end + +--- Remove client from urgent stack. +-- +-- @function awful.urgent.delete +-- @client c The client object. +function urgent.delete(c) + for k, cl in ipairs(data) do + if c == cl then + table.remove(data, k) + break + end + end +end + +capi.client.connect_signal("property::urgent", urgent.add) +capi.client.connect_signal("focus", urgent.delete) +capi.client.connect_signal("unmanage", urgent.delete) + +return urgent diff --git a/lib/awful/ewmh.lua b/lib/awful/ewmh.lua index 0fffa6dd..de4af306 100644 --- a/lib/awful/ewmh.lua +++ b/lib/awful/ewmh.lua @@ -12,7 +12,6 @@ local client = client local screen = screen local ipairs = ipairs local math = math -local atag = require("awful.tag") local aclient = require("awful.client") local ewmh = {} @@ -182,7 +181,7 @@ function ewmh.tag(c, t) if not t then c.sticky = true else - c.screen = atag.getscreen(t) + c.screen = t.screen c:tags({ t }) end end diff --git a/lib/awful/key.lua b/lib/awful/key.lua index 30feb5dc..834f7198 100644 --- a/lib/awful/key.lua +++ b/lib/awful/key.lua @@ -10,7 +10,7 @@ -- Grab environment we need local setmetatable = setmetatable local ipairs = ipairs -local capi = { key = key } +local capi = { key = key, root = root } local util = require("awful.util") @@ -26,6 +26,40 @@ local key = { mt = {}, hotkeys = {} } -- @class table local ignore_modifiers = { "Lock", "Mod2" } +--- Convert the modifiers into pc105 key names +local conversion = { + mod4 = "Super_L", + control = "Control_L", + shift = "Shift_L", + mod1 = "Alt_L", +} + +--- Execute a key combination. +-- If an awesome keybinding is assigned to the combination, it should be +-- executed. +-- @see root.fake_input +-- @tparam table mod A modified table. Valid modifiers are: Any, Mod1, +-- Mod2, Mod3, Mod4, Mod5, Shift, Lock and Control. +-- @tparam string k The key +function key.execute(mod, k) + for _, v in ipairs(mod) do + local m = conversion[v:lower()] + if m then + root.fake_input("key_press", m) + end + end + + root.fake_input("key_press" , k) + root.fake_input("key_release", k) + + for _, v in ipairs(mod) do + local m = conversion[v:lower()] + if m then + root.fake_input("key_release", m) + end + end +end + --- Create a new key to use as binding. -- This function is useful to create several keys from one, because it will use -- the ignore_modifier variable to create several keys with and without the @@ -66,6 +100,7 @@ function key.new(mod, _key, press, release, data) data.mod = mod data.key = _key table.insert(key.hotkeys, data) + data.execute = function(_) key.execute(mod, _key) end return ret end diff --git a/lib/awful/layout/init.lua b/lib/awful/layout/init.lua index 984fdde9..b1da60e6 100644 --- a/lib/awful/layout/init.lua +++ b/lib/awful/layout/init.lua @@ -11,7 +11,6 @@ local ipairs = ipairs local type = type local util = require("awful.util") -local ascreen = require("awful.screen") local capi = { screen = screen, mouse = mouse, @@ -21,6 +20,7 @@ local capi = { } local tag = require("awful.tag") local client = require("awful.client") +local ascreen = require("awful.screen") local timer = require("gears.timer") local function get_screen(s) @@ -64,7 +64,8 @@ local delayed_arrange = {} -- @param screen The screen. -- @return The layout function. function layout.get(screen) - local t = tag.selected(screen) + screen = screen or capi.mouse.screen + local t = get_screen(screen).selected_tag return tag.getproperty(t, "layout") or layout.suit.floating end @@ -78,8 +79,8 @@ function layout.inc(i, s, layouts) -- this was changed so that 'layouts' can be an optional parameter layouts, i, s = i, s, layouts end - s = get_screen(s) - local t = tag.selected(s) + s = get_screen(s or ascreen.focused()) + local t = s.selected_tag layouts = layouts or layout.layouts if t then local curlayout = layout.get(s) @@ -109,10 +110,10 @@ end --- Set the layout function of the current tag. -- @param _layout Layout name. --- @param t The tag to modify, if null tag.selected() is used. +-- @tparam[opt=mouse.screen.selected_tag] tag t The tag to modify. function layout.set(_layout, t) - t = t or tag.selected() - tag.setproperty(t, "layout", _layout) + t = t or capi.mouse.screen.selected_tag + t.layout = _layout end --- Get the layout parameters used for the screen @@ -130,24 +131,24 @@ end -- "geometries" table with client as keys and geometry as value function layout.parameters(t, screen) screen = get_screen(screen) - t = t or tag.selected(screen) + t = t or screen.selected_tag - screen = get_screen(t and tag.getscreen(t) or 1) + screen = get_screen(t and t.screen or 1) local p = {} - local useless_gap = t and tag.getgap(t, #client.tiled(screen)) or 0 + local useless_gap = t and t.gap or 0 - p.workarea = ascreen.get_bounding_geometry(screen, { + p.workarea = screen:get_bounding_geometry { honor_padding = true, honor_workarea = true, margins = useless_gap, - }) + } p.geometry = screen.geometry p.clients = client.tiled(screen) p.screen = screen.index - p.padding = ascreen.padding(screen) + p.padding = screen.padding p.useless_gap = useless_gap return p @@ -213,14 +214,14 @@ capi.client.connect_signal("property::screen", function(c, old_screen) end) local function arrange_tag(t) - layout.arrange(tag.getscreen(t)) + layout.arrange(t.screen) end capi.screen.add_signal("arrange") -capi.tag.connect_signal("property::mwfact", arrange_tag) -capi.tag.connect_signal("property::nmaster", arrange_tag) -capi.tag.connect_signal("property::ncol", arrange_tag) +capi.tag.connect_signal("property::master_width_factor", arrange_tag) +capi.tag.connect_signal("property::master_count", arrange_tag) +capi.tag.connect_signal("property::column_count", arrange_tag) capi.tag.connect_signal("property::layout", arrange_tag) capi.tag.connect_signal("property::windowfact", arrange_tag) capi.tag.connect_signal("property::selected", arrange_tag) diff --git a/lib/awful/layout/suit/corner.lua b/lib/awful/layout/suit/corner.lua index 6d740567..2327186a 100644 --- a/lib/awful/layout/suit/corner.lua +++ b/lib/awful/layout/suit/corner.lua @@ -11,6 +11,7 @@ -- Grab environment we need local ipairs = ipairs local math = math +local capi = {screen = screen} local tag = require("awful.tag") --- Actually arrange clients of p.clients for corner layout @@ -19,7 +20,7 @@ local tag = require("awful.tag") -- @param orientation String indicating in which corner is the master window. -- Available values are : NE, NW, SW, SE local function do_corner(p, orientation) - local t = p.tag or tag.selected(p.screen) + local t = p.tag or capi.screen[p.screen].selected_tag local wa = p.workarea local cls = p.clients @@ -29,9 +30,9 @@ local function do_corner(p, orientation) local column = {} local row = {} -- Use the nmaster field of the tag in a cheaty way - local row_privileged = ((tag.getnmaster(tag.selected(cls[1].screen)) % 2) == 0) + local row_privileged = ((cls[1].screen.selected_tag.master_count % 2) == 0) - local master_factor = tag.getmwfact(tag.selected(cls[1].screen)) + local master_factor = cls[1].screen.selected_tag.master_width_factor master.width = master_factor * wa.width master.height = master_factor * wa.height diff --git a/lib/awful/layout/suit/magnifier.lua b/lib/awful/layout/suit/magnifier.lua index 32482369..7842f72a 100644 --- a/lib/awful/layout/suit/magnifier.lua +++ b/lib/awful/layout/suit/magnifier.lua @@ -10,7 +10,6 @@ -- Grab environment we need local ipairs = ipairs local math = math -local tag = require("awful.tag") local capi = { client = client, @@ -18,7 +17,6 @@ local capi = mouse = mouse, mousegrabber = mousegrabber } -local client = require("awful.client") local magnifier = {} @@ -41,7 +39,8 @@ function magnifier.mouse_resize_handler(c, corner, x, y) -- New master width factor local mwfact = dist / maxdist_pow - tag.setmwfact(math.min(math.max(0.01, mwfact), 0.99), tag.selected(c.screen)) + c.screen.selected_tag.master_width_factor + = math.min(math.max(0.01, mwfact), 0.99) return true end end @@ -54,8 +53,8 @@ function magnifier.arrange(p) local area = p.workarea local cls = p.clients local focus = p.focus or capi.client.focus - local t = p.tag or tag.selected(p.screen) - local mwfact = tag.getmwfact(t) + local t = p.tag or capi.screen[p.screen].selected_tag + local mwfact = t.master_width_factor local fidx -- Check that the focused window is on the right screen @@ -67,7 +66,7 @@ function magnifier.arrange(p) end -- If focused window is not tiled, take the first one which is tiled. - if client.floating.get(focus) then + if focus.floating then focus = cls[1] fidx = 1 end diff --git a/lib/awful/layout/suit/tile.lua b/lib/awful/layout/suit/tile.lua index 0071ff56..802fdce9 100644 --- a/lib/awful/layout/suit/tile.lua +++ b/lib/awful/layout/suit/tile.lua @@ -29,7 +29,7 @@ tile.resize_jump_to_corner = true local function mouse_resize_handler(c, _, _, _, orientation) orientation = orientation or "tile" local wa = capi.screen[c.screen].workarea - local mwfact = tag.getmwfact() + local mwfact = c.screen.selected_tag.master_width_factor local cursor local g = c:geometry() local offset = 0 @@ -126,7 +126,8 @@ local function mouse_resize_handler(c, _, _, _, orientation) wfact = wfact_x end - tag.setmwfact(math.min(math.max(new_mwfact, 0.01), 0.99), tag.selected(c.screen)) + c.screen.selected_tag.master_width_factor + = math.min(math.max(new_mwfact, 0.01), 0.99) client.setwfact(math.min(math.max(wfact,0.01), 0.99), c) return true end @@ -195,7 +196,7 @@ local function tile_group(gs, cls, wa, orientation, fact, group) end local function do_tile(param, orientation) - local t = param.tag or tag.selected(param.screen) + local t = param.tag or capi.screen[param.screen].selected_tag orientation = orientation or "right" -- This handles all different orientations. @@ -208,12 +209,12 @@ local function do_tile(param, orientation) local gs = param.geometries local cls = param.clients - local nmaster = math.min(tag.getnmaster(t), #cls) + local nmaster = math.min(t.master_count, #cls) local nother = math.max(#cls - nmaster,0) - local mwfact = tag.getmwfact(t) + local mwfact = t.master_width_factor local wa = param.workarea - local ncol = tag.getncol(t) + local ncol = t.column_count local data = tag.getdata(t).windowfact @@ -229,7 +230,7 @@ local function do_tile(param, orientation) place_master = false end - local grow_master = tag.getmfpol(t) == "expand" + local grow_master = t.master_fill_policy == "expand" -- this was easier than writing functions because there is a lot of data we need for _ = 1,2 do if place_master and nmaster > 0 then diff --git a/lib/awful/menu.lua b/lib/awful/menu.lua index e444c06b..0dc62269 100644 --- a/lib/awful/menu.lua +++ b/lib/awful/menu.lua @@ -609,7 +609,7 @@ end -- terms[i] = -- {c.name, -- function() --- awful.tag.viewonly(c.first_tag) +-- c.first_tag:view_only() -- client.focus = c -- end, -- c.icon diff --git a/lib/awful/mouse/init.lua b/lib/awful/mouse/init.lua index 285b3aec..b1e362e0 100644 --- a/lib/awful/mouse/init.lua +++ b/lib/awful/mouse/init.lua @@ -111,13 +111,13 @@ function mouse.client.snap(c, snap, x, y, fixed_x, fixed_y) -- Allow certain windows to snap to the edge of the workarea. -- Only allow docking to workarea for consistency/to avoid problems. - if aclient.dockable.get(c) then + if c.dockable then local struts = c:struts() struts['left'] = 0 struts['right'] = 0 struts['top'] = 0 struts['bottom'] = 0 - if edge ~= "none" and aclient.floating.get(c) then + if edge ~= "none" and c.floating then if edge == "left" or edge == "right" then struts[edge] = cur_geom.width elseif edge == "top" or edge == "bottom" then @@ -177,7 +177,7 @@ function mouse.client.move(c, snap, finished_cb) for _, v in ipairs(_mouse.buttons) do if v then local lay = layout.get(c.screen) - if lay == layout.suit.floating or aclient.floating.get(c) then + if lay == layout.suit.floating or c.floating then local x = _mouse.x - dist_x local y = _mouse.y - dist_y c:geometry(mouse.client.snap(c, snap, x, y, fixed_x, fixed_y)) @@ -195,7 +195,7 @@ function mouse.client.move(c, snap, finished_cb) end if layout.get(c.screen) ~= layout.suit.floating then local c_u_m = mouse.client_under_pointer() - if c_u_m and not aclient.floating.get(c_u_m) then + if c_u_m and not c_u_m.floating then if c_u_m ~= c then c:swap(c_u_m) end @@ -229,8 +229,8 @@ function mouse.client.dragtotag.border(c) capi.mouse.coords({ x = wa.x + 1 }) end if not button_down then - local tags = tag.gettags(c.screen) - local t = tag.selected() + local tags = c.screen.tags + local t = c.screen.selected_tag local idx for i, v in ipairs(tags) do if v == t then @@ -239,11 +239,11 @@ function mouse.client.dragtotag.border(c) end if _mouse.x > wa.x + wa.width - 10 then local newtag = tags[util.cycle(#tags, idx + 1)] - aclient.movetotag(newtag, c) + c:move_to_tag(newtag) tag.viewnext() elseif _mouse.x < wa.x + 10 then local newtag = tags[util.cycle(#tags, idx - 1)] - aclient.movetotag(newtag, c) + c:move_to_tag(newtag) tag.viewprev() end return false @@ -356,7 +356,7 @@ function mouse.client.resize(c, corner) local lay = layout.get(c.screen) local corner2, x, y = mouse.client.corner(c, corner) - if lay == layout.suit.floating or aclient.floating.get(c) then + if lay == layout.suit.floating or c.floating then return layout.suit.floating.mouse_resize_handler(c, corner2, x, y) elseif lay.mouse_resize_handler then return lay.mouse_resize_handler(c, corner2, x, y) diff --git a/lib/awful/placement.lua b/lib/awful/placement.lua index 9555afd8..95866995 100644 --- a/lib/awful/placement.lua +++ b/lib/awful/placement.lua @@ -19,7 +19,7 @@ -- -- **honor_padding** (*boolean*): -- --- Take the screen padding into account (see `awful.screen.padding`) +-- Take the screen padding into account (see `screen.padding`) -- -- **tag** (*tag*): -- @@ -158,7 +158,7 @@ local function geometry_common(obj, args, new_geo, ignore_border_width) end -- It is a screen, it doesn't support setting new sizes. - return a_screen.get_bounding_geometry(obj, args) + return obj:get_bounding_geometry(args) else assert(false, "Invalid object") end @@ -453,7 +453,7 @@ function placement.no_overlap(c) local curlay = layout.get() local areas = { screen.workarea } for _, cl in pairs(cls) do - if cl ~= c and cl.type ~= "desktop" and (client.floating.get(cl) or curlay == layout.suit.floating) then + if cl ~= c and cl.type ~= "desktop" and (cl.floating or curlay == layout.suit.floating) then areas = area_remove(areas, area_common(cl)) end end diff --git a/lib/awful/rules.lua b/lib/awful/rules.lua index 26abc33e..42c6425b 100644 --- a/lib/awful/rules.lua +++ b/lib/awful/rules.lua @@ -13,8 +13,6 @@ local table = table local type = type local ipairs = ipairs local pairs = pairs -local aclient = require("awful.client") -local atag = require("awful.tag") local rules = {} @@ -199,13 +197,11 @@ function rules.execute(c, props, callbacks) if property ~= "focus" and type(value) == "function" then value = value(c) end - if property == "floating" then - aclient.floating.set(c, value) - elseif property == "tag" then - c.screen = atag.getscreen(value) + if property == "tag" then + c.screen = value.screen c:tags({ value }) elseif property == "switchtotag" and value and props.tag then - atag.viewonly(props.tag) + props.tag:view_only() elseif property == "height" or property == "width" or property == "x" or property == "y" then local geo = c:geometry(); diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua index c6662670..69943679 100644 --- a/lib/awful/screen.lua +++ b/lib/awful/screen.lua @@ -4,7 +4,7 @@ -- @author Julien Danjou <julien@danjou.info> -- @copyright 2008 Julien Danjou -- @release @AWESOME_VERSION@ --- @module awful.screen +-- @module screen --------------------------------------------------------------------------- -- Grab environment we need @@ -24,7 +24,7 @@ end -- we use require("awful.client") inside functions to prevent circular dependencies. local client -local screen = {} +local screen = {object={}} local data = {} data.padding = {} @@ -44,13 +44,27 @@ local function apply_geometry_ajustments(geo, delta) } end +--- Get the square distance between a `screen` and a point +-- @deprecated awful.screen.getdistance_sq -- @param s Screen -- @param x X coordinate of point -- @param y Y coordinate of point -- @return The squared distance of the screen to the provided point +-- @see screen.get_square_distance function screen.getdistance_sq(s, x, y) - s = get_screen(s) - local geom = s.geometry + util.deprecate "Use s:get_square_distance(x, y) instead of awful.screen.getdistance_sq" + + return screen.object.get_square_distance(s, x, y) +end + +--- Get the square distance between a `screen` and a point +-- @function screen.get_square_distance +-- @tparam number x X coordinate of point +-- @tparam number y Y coordinate of point +-- @treturn number The squared distance of the screen to the provided point +function screen.object.get_square_distance(self, x, y) + self = get_screen(self) + local geom = self.geometry local dist_x, dist_y = 0, 0 if x < geom.x then dist_x = geom.x - x @@ -69,13 +83,14 @@ end -- Return screen number corresponding to the given (pixel) coordinates. -- The number returned can be used as an index into the global -- `screen` table/object. +-- @function awful.screen.getbycoord -- @param x The x coordinate -- @param y The y coordinate function screen.getbycoord(x, y) local s = capi.screen[1] - local dist = screen.getdistance_sq(s, x, y) + local dist = screen.object.get_square_distance(s, x, y) for i in capi.screen do - local d = screen.getdistance_sq(i, x, y) + local d = screen.object.get_square_distance(i, x, y) if d < dist then s, dist = capi.screen[i], d end @@ -85,6 +100,7 @@ end --- Give the focus to a screen, and move pointer to last known position on this -- screen, or keep position relative to the current focused screen +-- @function awful.screen.focus -- @param _screen Screen number (defaults / falls back to mouse.screen). function screen.focus(_screen) client = client or require("awful.client") @@ -123,6 +139,7 @@ end --- Give the focus to a screen, and move pointer to last known position on this -- screen, or keep position relative to the current focused screen +-- @function awful.screen.focus_bydirection -- @param dir The direction, can be either "up", "down", "left" or "right". -- @param _screen Screen. function screen.focus_bydirection(dir, _screen) @@ -141,6 +158,7 @@ end --- Give the focus to a screen, and move pointer to last known position on this -- screen, or keep position relative to the current focused screen +-- @function awful.screen.focus_relative -- @param i Value to add to the current focused screen index. 1 will focus next -- screen, -1 would focus the previous one. function screen.focus_relative(i) @@ -148,28 +166,40 @@ function screen.focus_relative(i) end --- Get or set the screen padding. +-- @deprecated awful.screen.padding -- @param _screen The screen object to change the padding on -- @param[opt=nil] padding The padding, a table with 'top', 'left', 'right' and/or -- 'bottom' or a number value to apply set the same padding on all sides. Can be -- nil if you only want to retrieve padding -- @treturn table A table with left, right, top and bottom number values. +-- @see padding function screen.padding(_screen, padding) - if type(padding) == "number" then - padding = { - left = padding, - right = padding, - top = padding, - bottom = padding, - } - end + util.deprecate "Use _screen.padding = value instead of awful.screen.padding" - _screen = get_screen(_screen) if padding then - data.padding[_screen] = padding - _screen:emit_signal("padding") + screen.object.set_padding(_screen, padding) end - local p = data.padding[_screen] or {} + return screen.object.get_padding(_screen) +end + +--- The screen padding. +-- Add a "buffer" section on each side of the screen where the tiled client +-- wont be. +-- +-- **Signal:** +-- +-- * *property::padding* +-- +-- @property padding +-- @param table +-- @tfield integer table.x The horizontal position +-- @tfield integer table.y The vertical position +-- @tfield integer table.width The width +-- @tfield integer table.height The height + +function screen.object.get_padding(self) + local p = data.padding[self] or {} -- Create a copy to avoid accidental mutation and nil values return { @@ -180,14 +210,36 @@ function screen.padding(_screen, padding) } end +function screen.object.set_padding(self, padding) + if type(padding) == "number" then + padding = { + left = padding, + right = padding, + top = padding, + bottom = padding, + } + end + + self = get_screen(self) + if padding then + data.padding[self] = padding + self:emit_signal("padding") + end +end + +--- The defaults arguments for `awful.screen.focused` +-- @tfield[opt=nil] table awful.screen.default_focused_args + --- Get the focused screen. -- -- It is possible to set `awful.screen.default_focused_args` to override the -- default settings. -- +-- @function awful.screen.focused -- @tparam[opt] table args --- @tparam[opt=false] table args.client Use the client screen instead of the +-- @tparam[opt=false] boolean args.client Use the client screen instead of the -- mouse screen. +-- @tparam[opt=true] boolean args.mouse Use the mouse screen -- @treturn ?screen The focused screen object, or `nil` in case no screen is -- present currently. function screen.focused(args) @@ -209,24 +261,31 @@ end -- * **bounding_rect**: A bounding rectangle. This parameter is incompatible with -- `honor_workarea`. -- --- @tparam[opt=mouse.screen] screen s A screen +-- @function screen.get_bounding_geometry -- @tparam[opt={}] table args The arguments -- @treturn table A table with *x*, *y*, *width* and *height*. -function screen.get_bounding_geometry(s, args) +-- @usage local geo = screen:get_bounding_geometry { +-- honor_padding = true, +-- honor_workarea = true, +-- margins = { +-- left = 20, +-- }, +-- } +function screen.object.get_bounding_geometry(self, args) args = args or {} -- If the tag has a geometry, assume it is right if args.tag then - s = args.tag.screen + self = args.tag.screen end - s = get_screen(s or capi.mouse.screen) + self = get_screen(self or capi.mouse.screen) local geo = args.bounding_rect or (args.parent and args.parent:geometry()) or - s[args.honor_workarea and "workarea" or "geometry"] + self[args.honor_workarea and "workarea" or "geometry"] if (not args.parent) and (not args.bounding_rect) and args.honor_padding then - local padding = screen.padding(s) + local padding = self.padding geo = apply_geometry_ajustments(geo, padding) end @@ -242,7 +301,100 @@ function screen.get_bounding_geometry(s, args) return geo end +--- Get the list of the screen visible clients. +-- +-- Minimized and unmanaged clients are not included in this list as they are +-- technically not on the screen. +-- +-- The clients on tags currently not visible are not part of this list. +-- +-- @property clients +-- @param table The clients list, ordered top to bottom +-- @see all_clients +-- @see hidden_clients +-- @see client.get + +function screen.object.get_clients(s) + local cls = capi.client.get(s, true) + local vcls = {} + for _, c in pairs(cls) do + if c:isvisible() then + table.insert(vcls, c) + end + end + return vcls +end + +function screen.object.set_clients() end + +--- Get the list of the clients assigned to the screen but not currently +-- visible. +-- +-- This include minimized clients and clients on hidden tags. +-- +-- @property hidden_clients +-- @param table The clients list, ordered top to bottom +-- @see clients +-- @see all_clients +-- @see client.get + +function screen.object.get_hidden_clients(s) + local cls = capi.client.get(s, true) + local vcls = {} + for _, c in pairs(cls) do + if not c:isvisible() then + table.insert(vcls, c) + end + end + return vcls +end + +function screen.object.set_hidden_clients() end + +--- Get all clients assigned to the screen. +-- +-- @property all_clients +-- @param table The clients list, ordered top to bottom +-- @see clients +-- @see hidden_clients +-- @see client.get + +function screen.object.get_all_clients(s) + return capi.client.get(s, true) +end + +function screen.object.set_all_clients() end + +--- Get the list of the screen tiled clients. +-- +-- Same as s.clients, but excluding: +-- +-- * fullscreen clients +-- * maximized clients +-- * floating clients +-- +-- @property tiled_clients +-- @param table The clients list, ordered top to bottom + +function screen.object.get_tiled_clients(s) + local clients = s.clients + local tclients = {} + -- Remove floating clients + for _, c in pairs(clients) do + if not c.floating + and not c.fullscreen + and not c.maximized_vertical + and not c.maximized_horizontal then + table.insert(tclients, c) + end + end + return tclients +end + +function screen.object.set_tiled_clients() end + --- Call a function for each existing and created-in-the-future screen. +-- @function awful.screen.connect_for_each_screen -- @tparam function func The function to call. -- @tparam screen func.screen The screen function screen.connect_for_each_screen(func) @@ -253,15 +405,88 @@ function screen.connect_for_each_screen(func) end --- Undo the effect of connect_for_each_screen. +-- @function awful.screen.disconnect_for_each_screen -- @tparam function func The function that should no longer be called. function screen.disconnect_for_each_screen(func) capi.screen.disconnect_signal("added", func) end +--- A list of all tags on the screen. +-- +-- This property is read only, use `tag.screen`, `awful.tag.add`, `awful.tag.new` +-- or `t:delete()` to alter this list. +-- +-- @property tags +-- @param table +-- @treturn table A table with all available tags + +function screen.object.get_tags(s, unordered) + local tags = {} + + for _, t in ipairs(root.tags()) do + if get_screen(t.screen) == s then + table.insert(tags, t) + end + end + + -- Avoid infinite loop, + save some time + if not unordered then + table.sort(tags, function(a, b) + return (a.index or 9999) < (b.index or 9999) + end) + end + + return tags +end + +function screen.object.set_tags() end + +--- A list of all selected tags on the screen. +-- @property selected_tags +-- @param table +-- @treturn table A table with all selected tags. +-- @see tag.selected +-- @see client.to_selected_tags + +function screen.object.get_selected_tags(s) + local tags = screen.object.get_tags(s, true) + + local vtags = {} + for _, t in pairs(tags) do + if t.selected then + vtags[#vtags + 1] = t + end + end + return vtags +end + +function screen.object.set_selected_tags() end + +--- The first selected tag. +-- @property selected_tag +-- @param table +-- @treturn ?tag The first selected tag or nil +-- @see tag.selected +-- @see selected_tags + +function screen.object.get_selected_tag(s) + return screen.object.get_selected_tags(s)[1] +end + +function screen.object.set_selected_tag() end + + +--- When the tag history changed. +-- @signal tag::history::update + capi.screen.add_signal("padding") -- Extend the luaobject -object.properties(capi.screen, {auto_emit=true}) +object.properties(capi.screen, { + getter_class = screen.object, + setter_class = screen.object, + auto_emit = true, +}) return screen diff --git a/lib/awful/tag.lua b/lib/awful/tag.lua index 1a8b0a7a..b892f0af 100644 --- a/lib/awful/tag.lua +++ b/lib/awful/tag.lua @@ -4,7 +4,7 @@ -- @author Julien Danjou <julien@danjou.info> -- @copyright 2008 Julien Danjou -- @release @AWESOME_VERSION@ --- @module awful.tag +-- @module tag --------------------------------------------------------------------------- -- Grab environment we need @@ -29,10 +29,11 @@ local function get_screen(s) return s and capi.screen[s] end --- we use require("awful.client") inside functions to prevent circular dependencies. +-- awful.client is required() at the end of this file so the miss_handler is set +-- before it is being required. local client -local tag = { mt = {} } +local tag = {object = {}, mt = {} } -- Private data local data = {} @@ -44,54 +45,138 @@ data.tags = setmetatable({}, { __mode = 'k' }) tag.history = {} tag.history.limit = 20 ---- Move a tag to an absolute position in the screen[]:tags() table. --- @param new_index Integer absolute position in the table to insert. --- @param target_tag The tag that should be moved. If null, the currently --- selected tag is used. -function tag.move(new_index, target_tag) - target_tag = target_tag or tag.selected() - local scr = get_screen(tag.getscreen(target_tag)) - local tmp_tags = tag.gettags(scr) +-- screen.tags depend on index, it cannot be used by awful.tag +local function raw_tags(scr) + local tmp_tags = {} + for _, t in ipairs(root.tags()) do + if get_screen(t.screen) == scr then + table.insert(tmp_tags, t) + end + end - if (not new_index) or (new_index < 1) or (new_index > #tmp_tags) then + return tmp_tags +end + +--- The number of elements kept in the history. +-- @tfield integer awful.tag.history.limit + +--- The tag index. +-- +-- The index is the position as shown in the `awful.widget.taglist`. +-- +-- **Signal:** +-- +-- * *property::index* +-- +-- @property index +-- @param integer +-- @treturn number The tag index. + +function tag.object.set_index(self, idx) + local scr = get_screen(tag.getproperty(self, "screen")) + + -- screen.tags cannot be used as it depend on index + local tmp_tags = raw_tags(scr) + + if (not idx) or (idx < 1) or (idx > #tmp_tags) then return end local rm_index = nil for i, t in ipairs(tmp_tags) do - if t == target_tag then + if t == self then table.remove(tmp_tags, i) rm_index = i break end end - table.insert(tmp_tags, new_index, target_tag) - - for i=new_index < rm_index and new_index or rm_index, #tmp_tags do + table.insert(tmp_tags, idx, self) + for i = idx < rm_index and idx or rm_index, #tmp_tags do local tmp_tag = tmp_tags[i] - tag.setscreen(scr, tmp_tag) + tag.object.set_screen(tmp_tag, scr) tag.setproperty(tmp_tag, "index", i) end end +function tag.object.get_index(query_tag) + -- Get an unordered list of tags + local tags = raw_tags(query_tag.screen) + + local idx = tag.getproperty(query_tag, "index") + + if idx then return idx end + + -- Too bad, lets compute it + for i, t in ipairs(tags) do + if t == query_tag then + tag.setproperty(t, "index", i) + return i + end + end +end + +--- Move a tag to an absolute position in the screen[]:tags() table. +-- @deprecated awful.tag.move +-- @param new_index Integer absolute position in the table to insert. +-- @param target_tag The tag that should be moved. If null, the currently +-- selected tag is used. +-- @see index +function tag.move(new_index, target_tag) + util.deprecate("Use t.index = new_index instead of awful.tag.move") + + target_tag = target_tag or ascreen.focused().selected_tag + tag.object.set_index(target_tag, new_index) +end + --- Swap 2 tags +-- @function tag.swap +-- @see tag.swap +-- @param tag2 The second tag +-- @see client.swap +function tag.object.swap(self, tag2) + local idx1, idx2 = tag.object.get_index(self), tag.object.get_index(tag2) + local scr2, scr1 = tag.getproperty(tag2, "screen"), tag.getproperty(self, "screen") + + -- If they are on the same screen, avoid recomputing the whole table + -- for nothing. + if scr1 == scr2 then + tag.setproperty(self, "index", idx2) + tag.setproperty(tag2, "index", idx1) + else + tag.object.set_screen(tag2, scr1) + tag.object.set_index (tag2, idx1) + tag.object.set_screen(self, scr2) + tag.object.set_index (self, idx2) + end +end + +--- Swap 2 tags +-- @deprecated awful.tag.swap +-- @see tag.swap -- @param tag1 The first tag -- @param tag2 The second tag function tag.swap(tag1, tag2) - local idx1, idx2 = tag.getidx(tag1), tag.getidx(tag2) - local src2, src1 = tag.getscreen(tag2), tag.getscreen(tag1) - tag.setscreen(src1, tag2) - tag.move(idx1, tag2) - tag.setscreen(src2, tag1) - tag.move(idx2, tag1) + util.deprecate("Use t:swap(tag2) instead of awful.tag.swap") + + tag.object.swap(tag1, tag2) end --- Add a tag. +-- +-- This function allow to create tags from a set of properties: +-- +-- local t = awful.tag.add("my new tag", { +-- screen = screen.primary, +-- layout = awful.layout.suit.max, +-- }) +-- +-- @function awful.tag.add -- @param name The tag name, a string -- @param props The tags inital properties, a table -- @return The created tag +-- @see tag.delete function tag.add(name, props) local properties = props or {} @@ -100,9 +185,8 @@ function tag.add(name, props) -- set properties cannot be used as this has to be set before the first -- signal is sent properties.screen = get_screen(properties.screen or ascreen.focused()) - -- Index is also required - properties.index = (#tag.gettags(properties.screen))+1 + properties.index = #raw_tags(properties.screen)+1 local newtag = capi.tag{ name = name } @@ -112,13 +196,20 @@ function tag.add(name, props) newtag.activated = true for k, v in pairs(properties) do - tag.setproperty(newtag, k, v) + -- `rawget` doesn't work on userdata, `:clients()` is the only relevant + -- entry. + if k == "clients" or tag.object[k] then + newtag[k](newtag, v) + else + newtag[k] = v + end end return newtag end --- Create a set of tags and attach it to a screen. +-- @function awful.tag.new -- @param names The tag name, in a table -- @param screen The tag screen, or 1 if not set. -- @param layout The layout or layout table to set for this tags by default. @@ -140,45 +231,54 @@ function tag.new(names, screen, layout) end --- Find a suitable fallback tag. +-- @function awful.tag.find_fallback -- @param screen The screen to look for a tag on. [awful.screen.focused()] -- @param invalids A table of tags we consider unacceptable. [selectedlist(scr)] function tag.find_fallback(screen, invalids) local scr = screen or ascreen.focused() - local t = invalids or tag.selectedlist(scr) + local t = invalids or scr.selected_tags - for _, v in pairs(tag.gettags(scr)) do + for _, v in pairs(scr.tags) do if not util.table.hasitem(t, v) then return v end end end --- Delete a tag. --- @param target_tag Optional tag object to delete. [selected()] --- @param fallback_tag Tag to assign stickied tags to. [~selected()] +-- +-- To delete the current tag: +-- +-- mouse.screen.selected_tag:delete() +-- +-- @function tag.delete +-- @see awful.tag.add +-- @see awful.tag.find_fallback +-- @tparam[opt=awful.tag.find_fallback()] tag fallback_tag Tag to assign +-- stickied tags to. -- @return Returns true if the tag is successfully deleted, nil otherwise. -- If there are no clients exclusively on this tag then delete it. Any -- stickied clients are assigned to the optional 'fallback_tag'. -- If after deleting the tag there is no selected tag, try and restore from -- history or select the first tag on the screen. -function tag.delete(target_tag, fallback_tag) - -- abort if no tag is passed or currently selected - target_tag = target_tag or tag.selected() - if target_tag == nil or target_tag.activated == false then return end +function tag.object.delete(self, fallback_tag) - local target_scr = get_screen(tag.getscreen(target_tag)) - local tags = tag.gettags(target_scr) - local idx = tag.getidx(target_tag) + -- abort if the taf isn't currently activated + if not self.activated then return end + + local target_scr = get_screen(tag.getproperty(self, "screen")) + local tags = target_scr.tags + local idx = tag.object.get_index(self) local ntags = #tags -- We can't use the target tag as a fallback. - if fallback_tag == target_tag then return end + if fallback_tag == self then return end -- No fallback_tag provided, try and get one. if fallback_tag == nil then - fallback_tag = tag.find_fallback(target_scr, {target_tag}) + fallback_tag = tag.find_fallback(target_scr, {self}) end -- Abort if we would have un-tagged clients. - local clients = target_tag:clients() + local clients = self:clients() if ( #clients > 0 and ntags <= 1 ) or fallback_tag == nil then return end -- Move the clients we can off of this tag. @@ -197,8 +297,8 @@ function tag.delete(target_tag, fallback_tag) end -- delete the tag - data.tags[target_tag].screen = nil - target_tag.activated = false + data.tags[self].screen = nil + self.activated = false -- Update all indexes for i=idx+1, #tags do @@ -206,21 +306,38 @@ function tag.delete(target_tag, fallback_tag) end -- If no tags are visible, try and view one. - if tag.selected(target_scr) == nil and ntags > 0 then + if target_scr.selected_tag == nil and ntags > 0 then tag.history.restore(nil, 1) - if tag.selected(target_scr) == nil then - tags[tags[1] == target_tag and 2 or 1].selected = true + if target_scr.selected_tag == nil then + tags[tags[1] == self and 2 or 1].selected = true end end return true end +--- Delete a tag. +-- @deprecated awful.tag.delete +-- @see tag.delete +-- @param target_tag Optional tag object to delete. [selected()] +-- @param fallback_tag Tag to assign stickied tags to. [~selected()] +-- @return Returns true if the tag is successfully deleted, nil otherwise. +-- If there are no clients exclusively on this tag then delete it. Any +-- stickied clients are assigned to the optional 'fallback_tag'. +-- If after deleting the tag there is no selected tag, try and restore from +-- history or select the first tag on the screen. +function tag.delete(target_tag, fallback_tag) + util.deprecate("Use t:delete(fallback_tag) instead of awful.tag.delete") + + return tag.object.delete(target_tag, fallback_tag) +end + --- Update the tag history. +-- @function awful.tag.history.update -- @param obj Screen object. function tag.history.update(obj) local s = get_screen(obj) - local curtags = tag.selectedlist(s) + local curtags = s.selected_tags -- create history table if not data.history[s] then data.history[s] = {} @@ -257,6 +374,7 @@ function tag.history.update(obj) end --- Revert tag history. +-- @function awful.tag.history.restore -- @param screen The screen. -- @param idx Index in history. Defaults to "previous" which is a special index -- toggling between last two selected sets of tags. Number (eg 1) will go back @@ -264,7 +382,7 @@ end function tag.history.restore(screen, idx) local s = get_screen(screen or ascreen.focused()) local i = idx or "previous" - local sel = tag.selectedlist(s) + local sel = s.selected_tags -- do nothing if history empty if not data.history[s] or not data.history[s][i] then return end -- if all tags been deleted, try next entry @@ -290,44 +408,41 @@ function tag.history.restore(screen, idx) end --- Get a list of all tags on a screen --- @param s Screen +-- @deprecated awful.tag.gettags +-- @tparam screen s Screen -- @return A table with all available tags +-- @see screen.tags function tag.gettags(s) - s = get_screen(s) - local tags = {} - for _, t in ipairs(root.tags()) do - if get_screen(tag.getscreen(t)) == s then - table.insert(tags, t) - end - end + util.deprecate("Use s.tags instead of awful.tag.gettags") - table.sort(tags, function(a, b) - return (tag.getproperty(a, "index") or 9999) < (tag.getproperty(b, "index") or 9999) - end) - return tags + s = get_screen(s) + + return s and s.tags or {} end ---- Set a tag's screen --- @param s Screen --- @param t tag object -function tag.setscreen(s, t) +--- The tag screen. +-- +-- **Signal:** +-- +-- * *property::screen* +-- +-- @property screen +-- @param screen +-- @see screen - -- For API consistency, the arguments have been swapped for Awesome 3.6 - if type(t) == "number" then - util.deprecate("tag.setscreen arguments are now (s, t) instead of (t, s)") - s, t = t, s - end +function tag.object.set_screen(t, s) s = get_screen(s or ascreen.focused()) local sel = tag.selected - local old_screen = tag.getproperty(t, "screen") + local old_screen = get_screen(tag.getproperty(t, "screen")) + if s == old_screen then return end -- Keeping the old index make very little sense when changing screen - tag.setproperty(t, "index", nil, true) + tag.setproperty(t, "index", nil) -- Change the screen - tag.setproperty(t, "screen", s, true) + tag.setproperty(t, "screen", s) -- Make sure the client's screen matches its tags for _,c in ipairs(t:clients()) do @@ -337,8 +452,8 @@ function tag.setscreen(s, t) -- Update all indexes for _,screen in ipairs {old_screen, s} do - for i,t2 in ipairs(tag.gettags(screen)) do - tag.setproperty(t2, "index", i, true) + for i,t2 in ipairs(screen.tags) do + tag.setproperty(t2, "index", i) end end @@ -348,66 +463,193 @@ function tag.setscreen(s, t) end end +--- Set a tag's screen +-- @deprecated awful.tag.setscreen +-- @see screen +-- @param s Screen +-- @param t tag object +function tag.setscreen(s, t) + -- For API consistency, the arguments have been swapped for Awesome 3.6 + -- this method is already deprecated, so be silent and swap the args + if type(t) == "number" then + s, t = t, s + end + + util.deprecate("Use t.screen = s instead of awful.tag.setscreen(t, s)") + + tag.object.set_screen(t, s) +end + --- Get a tag's screen +-- @deprecated awful.tag.getscreen +-- @see screen -- @param[opt] t tag object -- @return Screen number function tag.getscreen(t) - t = t or tag.selected() + util.deprecate("Use t.screen instead of awful.tag.setscreen(t, s)") + + -- A new getter is not required + + t = t or ascreen.focused().selected_tag local prop = tag.getproperty(t, "screen") return prop and prop.index end --- Return a table with all visible tags +-- @deprecated awful.tag.selectedlist -- @param s Screen. -- @return A table with all selected tags. +-- @see screen.selected_tags function tag.selectedlist(s) - local screen = s or ascreen.focused() - local tags = tag.gettags(screen) - local vtags = {} - for _, t in pairs(tags) do - if t.selected then - vtags[#vtags + 1] = t - end - end - return vtags + util.deprecate("Use s.selected_tags instead of awful.tag.selectedlist") + + s = get_screen(s or ascreen.focused()) + + return s.selected_tags end --- Return only the first visible tag. +-- @deprecated awful.tag.selected -- @param s Screen. +-- @see screen.selected_tag function tag.selected(s) - return tag.selectedlist(s)[1] + util.deprecate("Use s.selected_tag instead of awful.tag.selected") + + s = get_screen(s or ascreen.focused()) + + return s.selected_tag end ---- Set master width factor. --- @param mwfact Master width factor. --- @param t The tag to modify, if null tag.selected() is used. -function tag.setmwfact(mwfact, t) - t = t or tag.selected() +--- The tag master width factor. +-- +-- The master width factor is one of the 5 main properties used to configure +-- the `layout`. Each layout interpret (or ignore) this property differenly. +-- +-- See the layout suit documentation for information about how the master width +-- factor is used. +-- +-- **Signal:** +-- +-- * *property::mwfact* (deprecated) +-- * *property::master_width_factor* +-- +-- @property master_width_factor +-- @param number Between 0 and 1 +-- @see master_count +-- @see column_count +-- @see master_fill_policy +-- @see gap + +function tag.object.set_master_width_factor(t, mwfact) if mwfact >= 0 and mwfact <= 1 then - tag.setproperty(t, "mwfact", mwfact, true) + tag.setproperty(t, "mwfact", mwfact) + tag.setproperty(t, "master_width_factor", mwfact) end end +function tag.object.get_master_width_factor(t) + return tag.getproperty(t, "master_width_factor") or 0.5 +end + +--- Set master width factor. +-- @deprecated awful.tag.setmwfact +-- @see master_fill_policy +-- @see master_width_factor +-- @param mwfact Master width factor. +-- @param t The tag to modify, if null tag.selected() is used. +function tag.setmwfact(mwfact, t) + util.deprecate("Use t.master_width_factor = mwfact instead of awful.tag.setmwfact") + + tag.object.get_master_width_factor(t or ascreen.focused().selected_tag, mwfact) +end + --- Increase master width factor. +-- @function awful.tag.incmwfact +-- @see master_width_factor -- @param add Value to add to master width factor. -- @param t The tag to modify, if null tag.selected() is used. function tag.incmwfact(add, t) - tag.setmwfact(tag.getmwfact(t) + add, t) + t = t or t or ascreen.focused().selected_tag + tag.object.set_master_width_factor(t, tag.object.get_master_width_factor(t) + add) end --- Get master width factor. +-- @deprecated awful.tag.getmwfact +-- @see master_width_factor +-- @see master_fill_policy -- @param[opt] t The tag. function tag.getmwfact(t) - t = t or tag.selected() - return tag.getproperty(t, "mwfact") or 0.5 + util.deprecate("Use t.master_width_factor instead of awful.tag.getmwfact") + + return tag.object.get_master_width_factor(t or ascreen.focused().selected_tag) end ---- Set layout --- @param layout a layout table or a constructor function --- @param t The tag to modify --- @return The layout -function tag.setlayout(layout, t) +--- An ordered list of layouts. +-- `awful.tag.layout` Is usually defined in `rc.lua`. It store the list of +-- layouts used when selecting the previous and next layouts. This is the +-- default: +-- +-- -- Table of layouts to cover with awful.layout.inc, order matters. +-- awful.layout.layouts = { +-- awful.layout.suit.floating, +-- awful.layout.suit.tile, +-- awful.layout.suit.tile.left, +-- awful.layout.suit.tile.bottom, +-- awful.layout.suit.tile.top, +-- awful.layout.suit.fair, +-- awful.layout.suit.fair.horizontal, +-- awful.layout.suit.spiral, +-- awful.layout.suit.spiral.dwindle, +-- awful.layout.suit.max, +-- awful.layout.suit.max.fullscreen, +-- awful.layout.suit.magnifier, +-- awful.layout.suit.corner.nw, +-- -- awful.layout.suit.corner.ne, +-- -- awful.layout.suit.corner.sw, +-- -- awful.layout.suit.corner.se, +-- } +-- +-- @field awful.tag.layouts +--- The tag client layout. +-- +-- This property hold the layout. A layout can be either stateless or stateful. +-- Stateless layouts are used by default by Awesome. They tile clients without +-- any other overhead. They take an ordered list of clients and place them on +-- the screen. Stateful layouts create an object instance for each tags and +-- can store variables and metadata. Because of this, they are able to change +-- over time and be serialized (saved). +-- +-- Both types of layouts have valid usage scenarios. +-- +-- **Stateless layouts:** +-- +-- These layouts are stored in `awful.layout.suit`. They expose a table with 2 +-- fields: +-- +-- * **name** (*string*): The layout name. This should be unique. +-- * **arrange** (*function*): The function called when the clients need to be +-- placed. The only parameter is a table or arguments returned by +-- `awful.layout.parameters` +-- +-- **Stateful layouts:** +-- +-- The stateful layouts API is the same as stateless, but they are a function +-- returining a layout instead of a layout itself. They also should have an +-- `is_dynamic = true` property. If they don't, `awful.tag` will create a new +-- instance everytime the layout is set. If they do, the instance will be +-- cached and re-used. +-- +-- **Signal:** +-- +-- * *property::layout* +-- +-- @property layout +-- @see awful.tag.layouts +-- @tparam layout|function layout A layout table or a constructor function +-- @return The layout + +function tag.object.set_layout(t, layout) -- Check if the signature match a stateful layout if type(layout) == "function" or ( type(layout) == "table" @@ -421,7 +663,7 @@ function tag.setlayout(layout, t) local instance = data.dynamic_cache[t][layout] or layout(t) -- Always make sure the layout is notified it is enabled - if tag.selected(tag.getscreen(t)) == t and instance.wake_up then + if tag.getproperty(t, "screen").selected_tag == t and instance.wake_up then instance:wake_up() end @@ -433,115 +675,248 @@ function tag.setlayout(layout, t) layout = instance end - tag.setproperty(t, "layout", layout, true) + tag.setproperty(t, "layout", layout) return layout end +--- Set layout. +-- @deprecated awful.tag.setlayout +-- @see layout +-- @param layout a layout table or a constructor function +-- @param t The tag to modify +-- @return The layout +function tag.setlayout(layout, t) + util.deprecate("Use t.layout = layout instead of awful.tag.setlayout") + + return tag.object.set_layout(t, layout) +end + +--- Define if the tag must be deleted when the last client is untagged. +-- +-- This is useful to create "throw-away" tags for operation like 50/50 +-- side-by-side views. +-- +-- local t = awful.tag.add("Temporary", { +-- screen = client.focus.screen, +-- volatile = true, +-- clients = { +-- client.focus, +-- awful.client.focus.history.get(client.focus.screen, 1) +-- } +-- } +-- +-- **Signal:** +-- +-- * *property::volatile* +-- +-- @property volatile +-- @param boolean + +-- Volatile accessors are implicit + --- Set if the tag must be deleted when the last client is untagged +-- @deprecated awful.tag.setvolatile +-- @see volatile -- @tparam boolean volatile If the tag must be deleted when the last client is untagged -- @param t The tag to modify, if null tag.selected() is used. function tag.setvolatile(volatile, t) - tag.setproperty(t, "volatile", volatile, true) + util.deprecate("Use t.volatile = volatile instead of awful.tag.setvolatile") + + tag.setproperty(t, "volatile", volatile) end --- Get if the tag must be deleted when the last client closes +-- @deprecated awful.tag.getvolatile +-- @see volatile -- @param t The tag to modify, if null tag.selected() is used. -- @treturn boolean If the tag will be deleted when the last client is untagged function tag.getvolatile(t) + util.deprecate("Use t.volatile instead of awful.tag.getvolatile") + return tag.getproperty(t, "volatile") or false end ---- Set the spacing between clients --- @param useless_gap The spacing between clients --- @param t The tag to modify, if null tag.selected() is used. -function tag.setgap(useless_gap, t) - t = t or tag.selected() +--- The default gap. +-- +-- @beautiful beautiful.useless_gap +-- @param number (default: 0) +-- @see gap + +--- The gap (spacing, also called `useless_gap`) between clients. +-- +-- This property allow to waste space on the screen in the name of style, +-- unicorns and readability. +-- +-- **Signal:** +-- +-- * *property::useless_gap* +-- +-- @property gap +-- @param number The value has to be greater than zero. + +function tag.object.set_gap(t, useless_gap) if useless_gap >= 0 then - tag.setproperty(t, "useless_gap", useless_gap, true) + tag.setproperty(t, "useless_gap", useless_gap) end end +function tag.object.get_gap(t) + return tag.getproperty(t, "useless_gap") or beautiful.useless_gap or 0 +end + +--- Set the spacing between clients +-- @deprecated awful.tag.setgap +-- @see gap +-- @param useless_gap The spacing between clients +-- @param t The tag to modify, if null tag.selected() is used. +function tag.setgap(useless_gap, t) + util.deprecate("Use t.gap = useless_gap instead of awful.tag.setgap") + + tag.object.set_gap(t or ascreen.focused().selected_tag, useless_gap) +end + --- Increase the spacing between clients +-- @function awful.tag.incgap +-- @see gap -- @param add Value to add to the spacing between clients -- @param t The tag to modify, if null tag.selected() is used. function tag.incgap(add, t) - tag.setgap(tag.getgap(t) + add, t) + t = t or t or ascreen.focused().selected_tag + tag.object.set_gap(t, tag.object.get_gap(t) + add) end --- Get the spacing between clients. +-- @deprecated awful.tag.getgap +-- @see gap -- @tparam[opt=tag.selected()] tag t The tag. -- @tparam[opt] int numclients Number of (tiled) clients. Passing this will -- return 0 for a single client. You can override this function to change -- this behavior. function tag.getgap(t, numclients) - t = t or tag.selected() + util.deprecate("Use t.gap instead of awful.tag.getgap") + if numclients == 1 then return 0 end - return tag.getproperty(t, "useless_gap") or beautiful.useless_gap or 0 + + return tag.object.get_gap(t or ascreen.focused().selected_tag) +end + +--- Set size fill policy for the master client(s). +-- +-- **Signal:** +-- +-- * *property::master_fill_policy* +-- +-- @property master_fill_policy +-- @param string "expand" or "master_width_factor" + +function tag.object.get_master_fill_policy(t) + return tag.getproperty(t, "master_fill_policy") or "expand" end --- Set size fill policy for the master client(s) +-- @deprecated awful.tag.setmfpol +-- @see master_fill_policy -- @tparam string policy Can be set to -- "expand" (fill all the available workarea) or --- "mwfact" (fill only an area inside the master width factor) +-- "master_width_factor" (fill only an area inside the master width factor) -- @tparam[opt=tag.selected()] tag t The tag to modify function tag.setmfpol(policy, t) - t = t or tag.selected() - tag.setproperty(t, "master_fill_policy", policy, true) + util.deprecate("Use t.master_fill_policy = policy instead of awful.tag.setmfpol") + + t = t or ascreen.focused().selected_tag + tag.setproperty(t, "master_fill_policy", policy) end --- Toggle size fill policy for the master client(s) --- between "expand" and "mwfact" +-- between "expand" and "master_width_factor". +-- @function awful.tag.togglemfpol +-- @see master_fill_policy -- @tparam tag t The tag to modify, if null tag.selected() is used. function tag.togglemfpol(t) + t = t or ascreen.focused().selected_tag + if tag.getmfpol(t) == "expand" then - tag.setmfpol("mwfact", t) + tag.setproperty(t, "master_fill_policy", "master_width_factor") else - tag.setmfpol("expand", t) + tag.setproperty(t, "master_fill_policy", "expand") end end --- Get size fill policy for the master client(s) +-- @deprecated awful.tag.getmfpol +-- @see master_fill_policy -- @tparam[opt=tag.selected()] tag t The tag -- @treturn string Possible values are -- "expand" (fill all the available workarea, default one) or --- "mwfact" (fill only an area inside the master width factor) +-- "master_width_factor" (fill only an area inside the master width factor) function tag.getmfpol(t) - t = t or tag.selected() + util.deprecate("Use t.master_fill_policy instead of awful.tag.getmfpol") + + t = t or ascreen.focused().selected_tag return tag.getproperty(t, "master_fill_policy") or "expand" end --- Set the number of master windows. --- @param nmaster The number of master windows. --- @param[opt] t The tag. -function tag.setnmaster(nmaster, t) - t = t or tag.selected() +-- +-- **Signal:** +-- +-- * *property::nmaster* (deprecated) +-- * *property::master_count* (deprecated) +-- +-- @property master_count +-- @param integer nmaster Only positive values are accepted + +function tag.object.set_master_count(t, nmaster) if nmaster >= 0 then - tag.setproperty(t, "nmaster", nmaster, true) + tag.setproperty(t, "nmaster", nmaster) + tag.setproperty(t, "master_count", nmaster) end end +function tag.object.get_master_count(t) + return tag.getproperty(t, "master_count") or 1 +end + +--- +-- @deprecated awful.tag.setnmaster +-- @see master_count +-- @param nmaster The number of master windows. +-- @param[opt] t The tag. +function tag.setnmaster(nmaster, t) + util.deprecate("Use t.master_count = nmaster instead of awful.tag.setnmaster") + + tag.object.set_master_count(t or ascreen.focused().selected_tag, nmaster) +end + --- Get the number of master windows. +-- @deprecated awful.tag.getnmaster +-- @see master_count -- @param[opt] t The tag. function tag.getnmaster(t) - t = t or tag.selected() - return tag.getproperty(t, "nmaster") or 1 + util.deprecate("Use t.master_count instead of awful.tag.setnmaster") + + t = t or ascreen.focused().selected_tag + return tag.getproperty(t, "master_count") or 1 end --- Increase the number of master windows. +-- @function awful.tag.incnmaster +-- @see master_count -- @param add Value to add to number of master windows. -- @param[opt] t The tag to modify, if null tag.selected() is used. -- @tparam[opt=false] boolean sensible Limit nmaster based on the number of -- visible tiled windows? function tag.incnmaster(add, t, sensible) - if sensible then - client = client or require("awful.client") - local screen = get_screen(tag.getscreen(t)) - local ntiled = #client.tiled(screen) + t = t or ascreen.focused().selected_tag - local nmaster = tag.getnmaster(t) + if sensible then + local screen = get_screen(tag.getproperty(t, "screen")) + local ntiled = #screen.tiled_clients + + local nmaster = tag.object.get_master_count(t) if nmaster > ntiled then nmaster = ntiled end @@ -550,59 +925,109 @@ function tag.incnmaster(add, t, sensible) if newnmaster > ntiled then newnmaster = ntiled end - tag.setnmaster(newnmaster, t) + tag.object.set_master_count(t, newnmaster) else - tag.setnmaster(tag.getnmaster(t) + add, t) + tag.object.set_master_count(t, tag.object.get_master_count(t) + add) end end +--- Set the tag icon. +-- +-- **Signal:** +-- +-- * *property::icon* +-- +-- @property icon +-- @tparam path|surface icon The icon + +-- accessors are implicit. --- Set the tag icon +-- @deprecated awful.tag.seticon +-- @see icon -- @param icon the icon to set, either path or image object -- @param _tag the tag function tag.seticon(icon, _tag) - _tag = _tag or tag.selected() - tag.setproperty(_tag, "icon", icon, true) + util.deprecate("Use t.icon = icon instead of awful.tag.seticon") + + _tag = _tag or ascreen.focused().selected_tag + tag.setproperty(_tag, "icon", icon) end --- Get the tag icon +-- @deprecated awful.tag.geticon +-- @see icon -- @param _tag the tag function tag.geticon(_tag) - _tag = _tag or tag.selected() + util.deprecate("Use t.icon instead of awful.tag.geticon") + + _tag = _tag or ascreen.focused().selected_tag return tag.getproperty(_tag, "icon") end +--- Set the number of columns. +-- +-- **Signal:** +-- +-- * *property::ncol* (deprecated) +-- * *property::column_count* +-- +-- @property column_count +-- @tparam integer ncol Has to be greater than 1 + +function tag.object.set_column_count(t, ncol) + if ncol >= 1 then + tag.setproperty(t, "ncol", ncol) + tag.setproperty(t, "column_count", ncol) + end +end + +function tag.object.get_column_count(t) + return tag.getproperty(t, "column_count") or 1 +end + --- Set number of column windows. +-- @deprecated awful.tag.setncol +-- @see column_count -- @param ncol The number of column. -- @param t The tag to modify, if null tag.selected() is used. function tag.setncol(ncol, t) - t = t or tag.selected() + util.deprecate("Use t.column_count = new_index instead of awful.tag.setncol") + + t = t or ascreen.focused().selected_tag if ncol >= 1 then - tag.setproperty(t, "ncol", ncol, true) + tag.setproperty(t, "ncol", ncol) + tag.setproperty(t, "column_count", ncol) end end --- Get number of column windows. +-- @deprecated awful.tag.getncol +-- @see column_count -- @param[opt] t The tag. function tag.getncol(t) - t = t or tag.selected() - return tag.getproperty(t, "ncol") or 1 + util.deprecate("Use t.column_count instead of awful.tag.getncol") + + t = t or ascreen.focused().selected_tag + return tag.getproperty(t, "column_count") or 1 end --- Increase number of column windows. +-- @function awful.tag.incncol -- @param add Value to add to number of column windows. -- @param[opt] t The tag to modify, if null tag.selected() is used. --- @tparam[opt=false] boolean sensible Limit ncol based on the number of visible +-- @tparam[opt=false] boolean sensible Limit column_count based on the number of visible -- tiled windows? function tag.incncol(add, t, sensible) + t = t or ascreen.focused().selected_tag + if sensible then - client = client or require("awful.client") - local screen = get_screen(tag.getscreen(t)) - local ntiled = #client.tiled(screen) - local nmaster = tag.getnmaster(t) + local screen = get_screen(tag.getproperty(t, "screen")) + local ntiled = #screen.tiled_clients + local nmaster = tag.object.get_master_count(t) local nsecondary = ntiled - nmaster - local ncol = tag.getncol(t) + local ncol = tag.object.get_column_count(t) if ncol > nsecondary then ncol = nsecondary end @@ -611,34 +1036,41 @@ function tag.incncol(add, t, sensible) if newncol > nsecondary then newncol = nsecondary end - tag.setncol(newncol, t) + + tag.object.set_column_count(t, newncol) else - tag.setncol(tag.getncol(t) + add, t) + tag.object.set_column_count(t, tag.object.get_column_count(t) + add) end end --- View no tag. +-- @function awful.tag.viewnone -- @tparam[opt] int|screen screen The screen. function tag.viewnone(screen) - local tags = tag.gettags(screen or ascreen.focused()) + screen = screen or ascreen.focused() + local tags = screen.tags for _, t in pairs(tags) do t.selected = false end end --- View a tag by its taglist index. --- @param i The relative index to see. +-- +-- This is equivalent to `screen.tags[i]:view_only()` +-- @function awful.tag.viewidx +-- @see screen.tags +-- @param i The **relative** index to see. -- @param[opt] screen The screen. function tag.viewidx(i, screen) screen = get_screen(screen or ascreen.focused()) - local tags = tag.gettags(screen) + local tags = screen.tags local showntags = {} for _, t in ipairs(tags) do if not tag.getproperty(t, "hide") then table.insert(showntags, t) end end - local sel = tag.selected(screen) + local sel = screen.selected_tag tag.viewnone(screen) for k, t in ipairs(showntags) do if t == sel then @@ -649,54 +1081,65 @@ function tag.viewidx(i, screen) end --- Get a tag's index in the gettags() table. +-- @deprecated awful.tag.getidx +-- @see index -- @param query_tag The tag object to find. [selected()] -- @return The index of the tag, nil if the tag is not found. function tag.getidx(query_tag) - query_tag = query_tag or tag.selected() - if query_tag == nil then return end + util.deprecate("Use t.index instead of awful.tag.getidx") - for i, t in ipairs(tag.gettags(tag.getscreen(query_tag))) do - if t == query_tag then - return i - end - end + return tag.object.get_index(query_tag or ascreen.focused().selected_tag) end --- View next tag. This is the same as tag.viewidx(1). +-- @function awful.tag.viewnext -- @param screen The screen. function tag.viewnext(screen) return tag.viewidx(1, screen) end --- View previous tag. This is the same a tag.viewidx(-1). +-- @function awful.tag.viewprev -- @param screen The screen. function tag.viewprev(screen) return tag.viewidx(-1, screen) end --- View only a tag. --- @param t The tag object. -function tag.viewonly(t) - local tags = tag.gettags(tag.getscreen(t)) +-- @function tag.view_only +-- @see selected +function tag.object.view_only(self) + local tags = self.screen.tags -- First, untag everyone except the viewed tag. for _, _tag in pairs(tags) do - if _tag ~= t then + if _tag ~= self then _tag.selected = false end end -- Then, set this one to selected. -- We need to do that in 2 operations so we avoid flickering and several tag -- selected at the same time. - t.selected = true - capi.screen[tag.getscreen(t)]:emit_signal("tag::history::update") + self.selected = true + capi.screen[self.screen]:emit_signal("tag::history::update") +end + +--- View only a tag. +-- @deprecated awful.tag.viewonly +-- @see tag.view_only +-- @param t The tag object. +function tag.viewonly(t) + util.deprecate("Use t:view_only() instead of awful.tag.viewonly") + + tag.object.view_only(t) end --- View only a set of tags. +-- @function awful.tag.viewmore -- @param tags A table with tags to view only. -- @param[opt] screen The screen of the tags. function tag.viewmore(tags, screen) screen = get_screen(screen or ascreen.focused()) - local screen_tags = tag.gettags(screen) + local screen_tags = screen.tags for _, _tag in ipairs(screen_tags) do if not util.table.hasitem(tags, _tag) then _tag.selected = false @@ -709,13 +1152,19 @@ function tag.viewmore(tags, screen) end --- Toggle selection of a tag +-- @function awful.tag.viewtoggle +-- @see selected -- @tparam tag t Tag to be toggled function tag.viewtoggle(t) t.selected = not t.selected - capi.screen[tag.getscreen(t)]:emit_signal("tag::history::update") + capi.screen[tag.getproperty(t, "screen")]:emit_signal("tag::history::update") end --- Get tag data table. +-- +-- Do not use. +-- +-- @deprecated awful.tag.getdata -- @tparam tag _tag The tag. -- @return The data table. function tag.getdata(_tag) @@ -723,6 +1172,10 @@ function tag.getdata(_tag) end --- Get a tag property. +-- +-- Use `_tag.prop` directly. +-- +-- @deprecated awful.tag.getproperty -- @tparam tag _tag The tag. -- @tparam string prop The property name. -- @return The property. @@ -735,39 +1188,44 @@ end --- Set a tag property. -- This properties are internal to awful. Some are used to draw taglist, or to -- handle layout, etc. +-- +-- Use `_tag.prop = value` +-- +-- @deprecated awful.tag.setproperty -- @param _tag The tag. -- @param prop The property name. -- @param value The value. --- @param ignore_setters Ignore the setter function for "prop" (boolean) -function tag.setproperty(_tag, prop, value, ignore_setters) +function tag.setproperty(_tag, prop, value) if not data.tags[_tag] then data.tags[_tag] = {} end - if (not ignore_setters) and tag["set"..prop] then - tag["set"..prop](value, _tag) - else - if data.tags[_tag][prop] ~= value then - data.tags[_tag][prop] = value - _tag:emit_signal("property::" .. prop) - end + if data.tags[_tag][prop] ~= value then + data.tags[_tag][prop] = value + _tag:emit_signal("property::" .. prop) end end --- Tag a client with the set of current tags. +-- @deprecated awful.tag.withcurrent -- @param c The client to tag. function tag.withcurrent(c) + util.deprecate("Use c:to_selected_tags() instead of awful.tag.selectedlist") + + -- It can't use c:to_selected_tags() because awful.tag is loaded before + -- awful.client + local tags = {} for _, t in ipairs(c:tags()) do - if get_screen(tag.getscreen(t)) == get_screen(c.screen) then + if get_screen(tag.getproperty(t, "screen")) == get_screen(c.screen) then table.insert(tags, t) end end if #tags == 0 then - tags = tag.selectedlist(c.screen) + tags = c.screen.selected_tags end if #tags == 0 then - tags = tag.gettags(c.screen) + tags = c.screen.tags end if #tags ~= 0 then c:tags(tags) @@ -777,7 +1235,7 @@ end local function attached_connect_signal_screen(screen, sig, func) screen = get_screen(screen) capi.tag.connect_signal(sig, function(_tag) - if get_screen(tag.getscreen(_tag)) == screen then + if get_screen(tag.getproperty(_tag, "screen")) == screen then func(_tag) end end) @@ -785,6 +1243,8 @@ end --- Add a signal to all attached tags and all tags that will be attached in the -- future. When a tag is detached from the screen, its signal is removed. +-- +-- @function awful.tag.attached_connect_signal -- @param screen The screen concerned, or all if nil. function tag.attached_connect_signal(screen, ...) if screen then @@ -809,7 +1269,7 @@ capi.client.connect_signal("manage", function(c) c.screen = ascreen.focused() end end - c:connect_signal("property::screen", tag.withcurrent) + c:connect_signal("property::screen", function() c:to_selected_tags() end) end) -- Keep track of the number of urgent clients. @@ -834,7 +1294,7 @@ local function client_untagged(c, t) end if #t:clients() == 0 and tag.getproperty(t, "volatile") then - tag.delete(t) + tag.object.delete(t) end end @@ -846,28 +1306,41 @@ local function urgent_callback(c) end capi.client.connect_signal("property::urgent", urgent_callback) -capi.client.connect_signal("manage", tag.withcurrent) capi.client.connect_signal("untagged", client_untagged) capi.client.connect_signal("tagged", client_tagged) -capi.tag.connect_signal("request::select", tag.viewonly) +capi.tag.connect_signal("request::select", tag.object.view_only) capi.tag.add_signal("property::hide") capi.tag.add_signal("property::icon") capi.tag.add_signal("property::icon_only") capi.tag.add_signal("property::layout") capi.tag.add_signal("property::mwfact") +capi.tag.add_signal("property::master_width_factor") capi.tag.add_signal("property::useless_gap") capi.tag.add_signal("property::master_fill_policy") capi.tag.add_signal("property::ncol") +capi.tag.add_signal("property::column_count") capi.tag.add_signal("property::nmaster") +capi.tag.add_signal("property::master_count") capi.tag.add_signal("property::windowfact") capi.tag.add_signal("property::screen") capi.tag.add_signal("property::index") + +--- True when a tagged client is urgent +-- @signal property::urgent +-- @see client.urgent + +--- The number of urgent tagged clients +-- @signal property::urgent_count +-- @see client.urgent + capi.tag.add_signal("property::urgent") + capi.tag.add_signal("property::urgent_count") capi.tag.add_signal("property::volatile") capi.screen.add_signal("tag::history::update") + capi.screen.connect_signal("tag::history::update", tag.history.update) function tag.mt:__call(...) @@ -878,12 +1351,17 @@ end -- `awful.tag.setproperty` currently handle calling the setter method itself -- while `awful.tag.getproperty`. object.properties(capi.tag, { - getter_class = tag, - setter_class = tag, + getter_class = tag.object, + setter_class = tag.object, getter_fallback = tag.getproperty, - setter = tag.setproperty, + setter_fallback = tag.setproperty, }) +-- fix a load loop +client = require("awful.client") +capi.client.connect_signal("manage", function(c) client.object.to_selected_tags(c) end) + + return setmetatable(tag, tag.mt) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/titlebar.lua b/lib/awful/titlebar.lua index 3cb3305c..c44d1703 100644 --- a/lib/awful/titlebar.lua +++ b/lib/awful/titlebar.lua @@ -256,7 +256,7 @@ end --- Create a new float button for a client. -- @param c The client for which the button is wanted. function titlebar.widget.floatingbutton(c) - local widget = titlebar.widget.button(c, "floating", aclient.floating.get, aclient.floating.toggle) + local widget = titlebar.widget.button(c, "floating", aclient.object.get_floating, aclient.floating.toggle) c:connect_signal("property::floating", widget.update) return widget end diff --git a/lib/awful/widget/layoutbox.lua b/lib/awful/widget/layoutbox.lua index a72aa088..b328aee8 100644 --- a/lib/awful/widget/layoutbox.lua +++ b/lib/awful/widget/layoutbox.lua @@ -31,7 +31,7 @@ local function update(w, screen) end local function update_from_tag(t) - local screen = get_screen(tag.getscreen(t)) + local screen = get_screen(t.screen) local w = boxes[screen] if w then update(w, screen) diff --git a/lib/awful/widget/taglist.lua b/lib/awful/widget/taglist.lua index b0cd32c0..47ae781d 100644 --- a/lib/awful/widget/taglist.lua +++ b/lib/awful/widget/taglist.lua @@ -111,8 +111,8 @@ function taglist.taglist_label(t, args) text = text .. "" end if not taglist_disable_icon then - if tag.geticon(t) then - icon = surface.load(tag.geticon(t)) + if t.icon then + icon = surface.load(t.icon) end end @@ -121,7 +121,7 @@ end local function taglist_update(s, w, buttons, filter, data, style, update_function) local tags = {} - for _, t in ipairs(tag.gettags(s)) do + for _, t in ipairs(s.tags) do if not tag.getproperty(t, "hide") and filter(t) then table.insert(tags, t) end @@ -185,7 +185,7 @@ function taglist.new(screen, filter, buttons, style, update_function, base_widge end end local uc = function (c) return u(c.screen) end - local ut = function (t) return u(tag.getscreen(t)) end + local ut = function (t) return u(t.screen) end capi.client.connect_signal("focus", uc) capi.client.connect_signal("unfocus", uc) tag.attached_connect_signal(nil, "property::selected", ut) diff --git a/lib/awful/widget/tasklist.lua b/lib/awful/widget/tasklist.lua index 130a6723..43a4b566 100644 --- a/lib/awful/widget/tasklist.lua +++ b/lib/awful/widget/tasklist.lua @@ -79,7 +79,7 @@ local function tasklist_label(c, args, tb) else if c.maximized_horizontal then name = name .. maximized_horizontal end if c.maximized_vertical then name = name .. maximized_vertical end - if client.floating.get(c) then name = name .. floating end + if c.floating then name = name .. floating end end end @@ -88,6 +88,7 @@ local function tasklist_label(c, args, tb) else name = name .. (util.escape(c.name) or util.escape("")) end + local focused = capi.client.focus == c -- Handle transient_for: the first parent that does not skip the taskbar -- is considered to be focused, if the real client has skip_taskbar. @@ -98,6 +99,7 @@ local function tasklist_label(c, args, tb) end) == c then focused = true end + if focused then bg = bg_focus text = text .. ""..name.."" @@ -274,7 +276,7 @@ function tasklist.filter.currenttags(c, screen) if get_screen(c.screen) ~= screen then return false end -- Include sticky client too if c.sticky then return true end - local tags = tag.gettags(screen) + local tags = screen.tags for _, t in ipairs(tags) do if t.selected then local ctags = c:tags() @@ -300,7 +302,7 @@ function tasklist.filter.minimizedcurrenttags(c, screen) if not c.minimized then return false end -- Include sticky client if c.sticky then return true end - local tags = tag.gettags(screen) + local tags = screen.tags for _, t in ipairs(tags) do -- Select only minimized clients if t.selected then diff --git a/lib/gears/object/properties.lua b/lib/gears/object/properties.lua index 7d05ca86..9b988682 100644 --- a/lib/gears/object/properties.lua +++ b/lib/gears/object/properties.lua @@ -53,13 +53,14 @@ function object.capi_index_fallback(class, args) -- Look for a getter method if args.getter_class and args.getter_class[getter_prefix..prop] then return args.getter_class[getter_prefix..prop](cobj) + elseif args.getter_class and args.getter_class["is_"..prop] then + return args.getter_class["is_"..prop](cobj) end -- Make sure something like c:a_mutator() works if args.getter_class and args.getter_class[prop] then return args.getter_class[prop] end - -- In case there is already a "dumb" getter like `awful.tag.getproperty' if args.getter_fallback then return args.getter_fallback(cobj, prop) diff --git a/mouse.c b/mouse.c index 5fe94568..75301b56 100644 --- a/mouse.c +++ b/mouse.c @@ -20,6 +20,9 @@ */ /** awesome mouse API + * + * See also `mousegrabber` + * * @author Julien Danjou <julien@danjou.info> * @copyright 2008-2009 Julien Danjou * @release @AWESOME_VERSION@ @@ -35,10 +38,14 @@ /** Mouse library. * - * @field screen Mouse screen. * @table mouse */ +/** + * The `screen` under the cursor + * @field screen + */ + /** A table with X and Y coordinates. * @field x X coordinate. * @field y Y coordinate. diff --git a/objects/client.c b/objects/client.c index eee3c103..d530376e 100644 --- a/objects/client.c +++ b/objects/client.c @@ -1,4 +1,4 @@ -/* +/* * client.c - client management * * Copyright © 2007-2009 Julien Danjou @@ -19,7 +19,21 @@ * */ -/** awesome client API +/** A process window. + * + * Clients are the name used by Awesome (and X11) to refer to a window. An + * application can have multiple clients (like for dialogs) or none at all + * (like command line applications). Clients are usually grouped by classes. A + * class is the name used by X11 to help window manager distinguish between + * window and write rules for them. See the `xprop` command line application. A + * client also have a `type` and `size_hints` used to define its behavior. + * + * ![Client geometry](../images/client_geo.svg) + * + * The client `:geometry()` return a table with *x*, *y*, *width* and *height*. + * The area returned **exclude the border width**. All clients also have a + * `shape_bounding` and `shape_clip` used to "crop" the client content. Finally, + * each clients can have titlebars (see `awful.titlebar`). * * Furthermore to the classes described here, one can also use signals as * described in @{signals} and X properties as described in @{xproperties}. @@ -28,6 +42,43 @@ * the documentation generation, you get the real signal name by * removing the starting dot. * + * Accessing client objects can be done in multiple ways depending on the + * context. To get the current focused client, use: + * + * local c = client.focus + * + * if c then + * -- do something + * end + * + * To get a list of all clients, use `client:get` + * + * for _, c in ipairs(client.get()) do + * -- do something + * end + * + * To get a callback when a new client is added, use the `manage` signal: + * + * client.connect_signal("manage", function(c) + * -- do something + * end + * + * To be notified a property changed in a client, use: + * + * client.connect_signal("property::name", function(c) + * -- do something + * end + * + * To be notified when a property change for a specific client (assuming it is + * stored in the variable `c`), use: + * + * c:connect_signal("property::name", function() + * -- do something + * end + * + * To get all the clients for a screen, use either `screen.clients` or + * `screen.tiled_clients` + * * @author Julien Danjou <julien@danjou.info> * @copyright 2008-2009 Julien Danjou * @release @AWESOME_VERSION@ @@ -53,57 +104,529 @@ /** Client class. * - * @table class - * @field focus The focused `client.object`. + * @table object */ -/** Client object. +/** + * The focused `client` or nil (in case there is none). * - * @field window The X window id. - * @field name The client title. - * @field skip_taskbar True if the client does not want to be in taskbar. - * @field type The window type (desktop, normal, dock, …). - * @field class The client class. - * @field instance The client instance. - * @field pid The client PID, if available. - * @field role The window role, if available. - * @field machine The machine client is running on. - * @field icon_name The client name when iconified. - * @field icon The client icon. - * @field screen Client screen. - * @field hidden Define if the client must be hidden, i.e. never mapped, + * @tfield client focus + */ + +/** + * The X window id. + * + * **Signal:** + * + * * *property::window* + * + * @property window + * @param string + */ + +/** + * The client title. + * + * **Signal:** + * + * * *property::name* + * + * @property name + * @param string + */ + +/** + * True if the client does not want to be in taskbar. + * + * **Signal:** + * + * * *property::skip\_taskbar* + * + * @property skip_taskbar + * @param boolean + */ + +/** + * The window type. + * + * Valid types are: + * + * * **desktop**: The root client, it cannot be moved or resized. + * * **dock**: A client attached to the side of the screen. + * * **splash**: A client, usually without titlebar shown when an application starts. + * * **dialog**: A dialog, see `transient_for`. + * * **menu**: A context menu. + * * **toolbar**: A floating toolbar. + * * **utility**: + * * **dropdown_menu**: A context menu attached to a parent position. + * * **popup_menu**: A context menu. + * * **notification**: A notification popup. + * * **combo**: A combobox list menu. + * * **dnd**: A drag and drop indicator. + * * **normal**: A normal application main window. + * + * More information can be found [here](https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm140200472629520) + * + * **Signal:** + * + * * *property::type* + * + * @property type + * @param string + */ + +/** + * The client class. + * + * If the client has multiple classes, the first one is used. + * + * To get a client class from the command line, use te `xprop` command. + * + * **Signal:** + * + * * *property::class* + * + * @property class + * @param string + */ + +/** + * The client instance. + * + * **Signal:** + * + * * *property::instance* + * + * @property instance + * @param string + */ + +/** + * The client PID, if available. + * + * **Signal:** + * + * * *property::pid* + * + * @property pid + * @param number + */ + +/** + * The window role, if available. + * + * **Signal:** + * + * * *property::role* + * + * @property role + * @param string + */ + +/** + * The machine client is running on. + * + * **Signal:** + * + * * *property::machine* + * + * @property machine + * @param string + */ + +/** + * The client name when iconified. + * + * **Signal:** + * + * * *property::icon\_name* + * + * @property icon_name + * @param string + */ + +/** + * The client icon. + * + * **Signal:** + * + * * *property::icon* + * + * @property icon + * @param surface + */ + +/** + * Client screen. + * + * **Signal:** + * + * * *property::screen* + * + * @property screen + * @param screen + */ + +/** + * Define if the client must be hidden, i.e. never mapped, * invisible in taskbar. - * @field minimized Define it the client must be iconify, i.e. only visible in + * + * **Signal:** + * + * * *property::hidden* + * + * @property hidden + * @param boolean + */ + +/** + * Define it the client must be iconify, i.e. only visible in * taskbar. - * @field size_hints_honor Honor size hints, i.e. respect size ratio. - * @field border_width The client border width. - * @field border_color The client border color. - * @field urgent The client urgent state. - * @field content An image representing the client window content (screenshot). - * @field opacity The client opacity between 0 and 1. - * @field ontop The client is on top of every other windows. - * @field above The client is above normal windows. - * @field below The client is below normal windows. - * @field fullscreen The client is fullscreen or not. - * @field maximized The client is maximized (horizontally and vertically) or not. - * @field maximized_horizontal The client is maximized horizontally or not. - * @field maximized_vertical The client is maximized vertically or not. - * @field transient_for The client the window is transient for. - * @field group_window Window identification unique to a group of windows. - * @field leader_window Identification unique to windows spawned by the same command. - * @field size_hints A table with size hints of the client: `user_position`, - * `user_size`, `program_position`, `program_size`, etc. - * @field sticky Set the client sticky, i.e. available on all tags. - * @field modal Indicate if the client is modal. - * @field focusable True if the client can receive the input focus. - * @field shape_bounding The client's bounding shape as set by awesome as a (native) cairo surface. - * @field shape_clip The client's clip shape as set by awesome as a (native) cairo surface. - * @field shape_client_bounding The client's bounding shape as set by the program as a (native) cairo surface. - * @field shape_client_clip The client's clip shape as set by the program as a (native) cairo surface. - * @field startup_id The FreeDesktop StartId. - * @field valid If the client that this object refers to is still managed by awesome. - * @field first_tag The first tag of the client. Optimized form of `c:tags()[1]`. - * @table object + * + * **Signal:** + * + * * *property::minimized* + * + * @property minimized + * @param boolean + */ + +/** + * Honor size hints, e.g. respect size ratio. + * + * For example, a terminal such as `xterm` require the client size to be a + * multiple of the character size. Honoring size hints will cause the terminal + * window to have a small gap at the bottom. + * + * This is enabled by default. To disable it by default, see `awful.rules`. + * + * **Signal:** + * + * * *property::size\_hints\_honor* + * + * @property size_hints_honor + * @param boolean + * @see size_hints + */ + +/** + * The client border width. + * @property border_width + * @param integer + */ + +/** + * The client border color. + * + * **Signal:** + * + * * *property::border\_color* + * + * @see gears.color + * + * @property border_color + * @param pattern Any string, gradients and patterns will be converted to a + * cairo pattern. + */ + +/** + * The client urgent state. + * + * **Signal:** + * + * * *property::urgent* + * + * @property urgent + * @param boolean + */ + +/** + * A cairo surface for the client window content. + * + * To get the screenshot, use: + * + * gears.surface(c.content) + * + * To save it, use: + * + * gears.surface(c.content):write_to_png(path) + * + * @property content + * @param surface + */ + +/** + * The client opacity. + * + * **Signal:** + * + * * *property::opacity* + * + * @property opacity + * @param number Between 0 (transparent) to 1 (opaque) + */ + +/** + * The client is on top of every other windows. + * @property ontop + * @param boolean + */ + +/** + * The client is above normal windows. + * + * **Signal:** + * + * * *property::above* + * + * @property above + * @param boolean + */ + +/** + * The client is below normal windows. + * + * **Signal:** + * + * * *property::below* + * + * @property below + * @param boolean + */ + +/** + * The client is fullscreen or not. + * + * **Signal:** + * + * * *property::fullscreen* + * + * @property fullscreen + * @param boolean + */ + +/** + * The client is maximized (horizontally and vertically) or not. + * + * **Signal:** + * + * * *property::maximized* + * + * @property maximized + * @param boolean + */ + +/** + * The client is maximized horizontally or not. + * + * **Signal:** + * + * * *property::maximized\_horizontal* + * + * @property maximized_horizontal + * @param boolean + */ + +/** + * The client is maximized vertically or not. + * + * **Signal:** + * + * * *property::maximized\_vertical* + * + * @property maximized_vertical + * @param boolean + */ + +/** + * The client the window is transient for. + * + * **Signal:** + * + * * *property::transient\_for* + * + * @property transient_for + * @param client + */ + +/** + * Window identification unique to a group of windows. + * + * **Signal:** + * + * * *property::group\_window* + * + * @property group_window + * @param client + */ + +/** + * Identification unique to windows spawned by the same command. + * @property leader_window + * @param client + */ + +/** + * A table with size hints of the client. + * + * **Signal:** + * + * * *property::size\_hints* + * + * @property size_hints + * @param table + * @tfield integer table.user_position + * @tfield integer table.user_size + * @tfield integer table.program_position + * @tfield integer table.program_size + * @tfield integer table.max_width + * @tfield integer table.max_height + * @tfield integer table.min_width + * @tfield integer table.min_height + * @tfield integer table.width_inc + * @tfield integer table.height_inc + * @see size_hints_honor + */ + +/** + * Set the client sticky, i.e. available on all tags. + * + * **Signal:** + * + * * *property::sticky* + * + * @property sticky + * @param boolean + */ + +/** + * Indicate if the client is modal. + * + * **Signal:** + * + * * *property::modal* + * + * @property modal + * @param boolean + */ + +/** + * True if the client can receive the input focus. + * + * **Signal:** + * + * * *property::focusable* + * + * @property focusable + * @param boolean + */ + +/** + * The client's bounding shape as set by awesome as a (native) cairo surface. + * + * **Signal:** + * + * * *property::shape\_bounding* + * + * @see gears.surface.apply_shape_bounding + * @property shape_bounding + * @param surface + */ + +/** + * The client's clip shape as set by awesome as a (native) cairo surface. + * + * **Signal:** + * + * * *property::shape\_clip* + * + * @property shape_clip + * @param surface + */ + +/** + * The client's bounding shape as set by the program as a (native) cairo surface. + * + * **Signal:** + * + * * *property::shape\_client\_bounding* + * + * @property shape_client_bounding + * @param surface + */ + +/** + * The client's clip shape as set by the program as a (native) cairo surface. + * + * **Signal:** + * + * * *property::shape\_client\_clip* + * + * @property shape_client_clip + * @param surface + */ + +/** + * The FreeDesktop StartId. + * + * When a client is spawned (like using a terminal or `awful.spawn`, a startup + * notification identifier is created. When the client is created, this + * identifier remain the same. This allow to match a spawn event to an actual + * client. + * + * **Signal:** + * + * * *property::startup\_id* + * + * @property startup_id + * @param string + */ + +/** + * If the client that this object refers to is still managed by awesome. + * + * To avoid errors, use: + * + * local is_valid = pcall(function() return c.valid end) and c.valid + * + * **Signal:** + * + * * *property::valid* + * + * @property valid + * @param boolean + */ + +/** + * The first tag of the client. Optimized form of `c:tags()[1]`. + * + * **Signal:** + * + * * *property::first\_tag* + * + * @property first_tag + * @param tag + */ + +/** + * The border color when the client is focused. + * + * @beautiful beautiful.border_focus + * @param string + */ + +/** + * The border color when the client is not focused. + * + * @beautiful beautiful.border_normal + * @param string + */ + +/** + * The client border width. + * + * @beautiful beautiful.border_width + * @param integer */ /** Return client struts (reserved space at the edge of the screen). @@ -126,12 +649,12 @@ * @function instances */ -/** Set a __index metamethod for all client instances. +/* Set a __index metamethod for all client instances. * @tparam function cb The meta-method * @function set_index_miss_handler */ -/** Set a __newindex metamethod for all client instances. +/* Set a __newindex metamethod for all client instances. * @tparam function cb The meta-method * @function set_newindex_miss_handler */ @@ -2854,161 +3377,46 @@ client_class_setup(lua_State *L) * @signal mouse::move */ signal_add(&client_class.signals, "mouse::move"); - /** - * @signal property::above - */ + + /* Those signals are documented elsewhere */ signal_add(&client_class.signals, "property::above"); - /** - * @signal property::below - */ signal_add(&client_class.signals, "property::below"); - /** - * @signal property::class - */ signal_add(&client_class.signals, "property::class"); - /** - * @signal property::focusable - */ signal_add(&client_class.signals, "property::focusable"); - /** - * @signal property::fullscreen - */ signal_add(&client_class.signals, "property::fullscreen"); - /** - * @signal property::geometry - */ signal_add(&client_class.signals, "property::geometry"); - /** - * @signal property::group_window - */ signal_add(&client_class.signals, "property::group_window"); - /** - * @signal property::height - */ signal_add(&client_class.signals, "property::height"); - /** - * @signal property::hidden - */ signal_add(&client_class.signals, "property::hidden"); - /** - * @signal property::icon - */ signal_add(&client_class.signals, "property::icon"); - /** - * @signal property::icon_name - */ signal_add(&client_class.signals, "property::icon_name"); - /** - * @signal property::instance - */ signal_add(&client_class.signals, "property::instance"); - /** - * @signal property::keys - */ signal_add(&client_class.signals, "property::keys"); - /** - * @signal property::machine - */ signal_add(&client_class.signals, "property::machine"); - /** - * @signal property::maximized - */ signal_add(&client_class.signals, "property::maximized"); - /** - * @signal property::maximized_horizontal - */ signal_add(&client_class.signals, "property::maximized_horizontal"); - /** - * @signal property::maximized_vertical - */ signal_add(&client_class.signals, "property::maximized_vertical"); - /** - * @signal property::minimized - */ signal_add(&client_class.signals, "property::minimized"); - /** - * @signal property::modal - */ signal_add(&client_class.signals, "property::modal"); - /** - * @signal property::name - */ signal_add(&client_class.signals, "property::name"); - /** - * @signal property::ontop - */ signal_add(&client_class.signals, "property::ontop"); - /** - * @signal property::pid - */ signal_add(&client_class.signals, "property::pid"); - /** - * @signal property::role - */ signal_add(&client_class.signals, "property::role"); - /** - * @signal property::screen - */ signal_add(&client_class.signals, "property::screen"); - /** - * @signal property::shape_bounding - */ signal_add(&client_class.signals, "property::shape_bounding"); - /** - * @signal property::shape_client_bounding - */ signal_add(&client_class.signals, "property::shape_client_bounding"); - /** - * @signal property::shape_client_clip - */ signal_add(&client_class.signals, "property::shape_client_clip"); - /** - * @signal property::shape_clip - */ signal_add(&client_class.signals, "property::shape_clip"); - /** - * @signal property::size_hints_honor - */ signal_add(&client_class.signals, "property::size_hints_honor"); - /** - * @signal property::skip_taskbar - */ signal_add(&client_class.signals, "property::skip_taskbar"); - /** - * @signal property::sticky - */ signal_add(&client_class.signals, "property::sticky"); - /** - * @signal property::struts - */ signal_add(&client_class.signals, "property::struts"); - /** - * @signal property::titlebar_bottom - */ signal_add(&client_class.signals, "property::titlebar_bottom"); - /** - * @signal property::titlebar_left - */ signal_add(&client_class.signals, "property::titlebar_left"); - /** - * @signal property::titlebar_right - */ signal_add(&client_class.signals, "property::titlebar_right"); - /** - * @signal property::titlebar_top - */ signal_add(&client_class.signals, "property::titlebar_top"); - /** - * @signal property::transient_for - */ signal_add(&client_class.signals, "property::transient_for"); - /** - * @signal property::type - */ signal_add(&client_class.signals, "property::type"); - /** - * @signal property::urgent - */ signal_add(&client_class.signals, "property::urgent"); /** * @signal property::width diff --git a/objects/screen.c b/objects/screen.c index a41fb152..dd29394e 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -20,6 +20,22 @@ */ /** awesome screen API + * + * Screen objects can be added and removed over time. To get a callback for all + * current and future screens, use `awful.screen.connect_for_each_screen`: + * + * awful.screen.connect_for_each_screen(function(s) + * -- do something + * end) + * + * It is also possible loop over all current screens using: + * + * for s, screen do + * -- do something + * end + * + * Most basic Awesome objects also have a screen property, see `mouse.screen` + * `client.screen`, `wibox.screen` and `tag.screen`. * * Furthermore to the classes described here, one can also use signals as * described in @{signals}. @@ -47,26 +63,91 @@ * The primary screen can be accessed as `screen.primary`. * Each screen has a set of properties. * - * @tfield table geometry The screen coordinates. Immutable. - * @tfield table workarea The screen workarea. - * @tfield int index The screen number. - * @tfield table outputs If RANDR information is available, a list of outputs - * for this screen and their size in mm. - * @table screen */ + /** + * The primary screen. + * + * @tfield screen primary + */ + +/** + * The screen coordinates. + * + * **Immutable:** true + * @property geometry + * @param table + * @tfield integer table.x The horizontal position + * @tfield integer table.y The vertical position + * @tfield integer table.width The width + * @tfield integer table.height The height + */ + +/** + * The screen number. + * + * An integer greater than 1 and smaller than `screen.count()`. Please note that + * the screen order isn't always mirroring the screen logical position. + * + * **Immutable:** true + * @property index + * @param integer + */ + +/** + * If RANDR information is available, a list of outputs + * for this screen and their size in mm. + * + * Please note that the table content may vary. + * + * **Signal:** + * + * * *property::outputs* + * + * **Immutable:** true + * @property outputs + * @param table + * @tfield table table.name A table with the screen name as key (like `eDP1` on a laptop) + * @tfield integer table.name.mm_width The screen physical width + * @tfield integer table.name.mm_height The screen physical height + */ + +/** + * The screen workarea. + * + * The workarea is a subsection of the screen where clients can be placed. It + * usually excludes the toolbars (see `awful.wibox`) and dockable clients + * (see `client.dockable`) like WindowMaker DockAPP. + * + * It can be modified be altering the `wibox` or `client` struts. + * + * **Signal:** + * + * * *property::workarea* + * + * @property workarea + * @see client.struts + * @see drawin.struts + * @param table + * @tfield integer table.x The horizontal position + * @tfield integer table.y The vertical position + * @tfield integer table.width The width + * @tfield integer table.height The height + */ + + /** Get the number of instances. * * @return The number of screen objects alive. * @function instances */ -/** Set a __index metamethod for all screen instances. +/* Set a __index metamethod for all screen instances. * @tparam function cb The meta-method * @function set_index_miss_handler */ -/** Set a __newindex metamethod for all screen instances. +/* Set a __newindex metamethod for all screen instances. * @tparam function cb The meta-method * @function set_newindex_miss_handler */ @@ -752,17 +833,15 @@ screen_class_setup(lua_State *L) NULL, (lua_class_propfunc_t) luaA_screen_get_workarea, NULL); - /** - * @signal property::workarea - */ + signal_add(&screen_class.signals, "property::workarea"); /** - * @signal primary_changed + * @signal .primary_changed */ signal_add(&screen_class.signals, "primary_changed"); /** * This signal is emitted when a new screen is added to the current setup. - * @signal added + * @signal .added */ signal_add(&screen_class.signals, "added"); } diff --git a/objects/tag.c b/objects/tag.c index 590413d4..92396dec 100644 --- a/objects/tag.c +++ b/objects/tag.c @@ -24,6 +24,8 @@ * Furthermore to the classes described here, one can also use signals as * described in @{signals}. * + * ![Client geometry](../images/tag_props.svg) + * * Some signal names are starting with a dot. These dots are artefacts from * the documentation generation, you get the real signal name by * removing the starting dot. @@ -40,12 +42,37 @@ #include "ewmh.h" #include "luaa.h" -/** Tag object. +/** + * Tag name. * - * @field name Tag name. - * @field selected True if the tag is selected to be viewed. - * @field activated True if the tag is active and can be used. - * @table tag + * **Signal:** + * + * * *property::name* + * + * @property name + * @param string + */ + +/** + * True if the tag is selected to be viewed + * + * **Signal:** + * + * * *property::selected* + * + * @property selected + * @param boolean + */ + +/** + * True if the tag is active and can be used. + * + * **Signal:** + * + * * *property::activated* + * + * @property activated + * @param boolean */ /** Get the number of instances. @@ -54,12 +81,12 @@ * @function instances */ -/** Set a __index metamethod for all tag instances. +/* Set a __index metamethod for all tag instances. * @tparam function cb The meta-method * @function set_index_miss_handler */ -/** Set a __newindex metamethod for all tag instances. +/* Set a __newindex metamethod for all tag instances. * @tparam function cb The meta-method * @function set_newindex_miss_handler */ @@ -389,17 +416,8 @@ tag_class_setup(lua_State *L) (lua_class_propfunc_t) luaA_tag_get_activated, (lua_class_propfunc_t) luaA_tag_set_activated); - /** - * @signal property::name - */ signal_add(&tag_class.signals, "property::name"); - /** - * @signal property::selected - */ signal_add(&tag_class.signals, "property::selected"); - /** - * @signal property::activated - */ signal_add(&tag_class.signals, "property::activated"); /** * @signal request::select diff --git a/tests/examples/shims/awesome.lua b/tests/examples/shims/awesome.lua index 30c2c39b..41c458a9 100644 --- a/tests/examples/shims/awesome.lua +++ b/tests/examples/shims/awesome.lua @@ -8,7 +8,7 @@ local function _shim_fake_class() local meta = { __index = function()end, - __new_index = function()end, + __newindex = function()end, } obj._connect_signal = obj.connect_signal @@ -28,7 +28,7 @@ local function _shim_fake_class() end function obj.set_newindex_miss_handler(handler) - meta.__new_index = handler + meta.__newindex = handler end function obj.emit_signal(name, c, ...) diff --git a/tests/examples/shims/client.lua b/tests/examples/shims/client.lua index f4f78d1d..f2dd0d38 100644 --- a/tests/examples/shims/client.lua +++ b/tests/examples/shims/client.lua @@ -2,7 +2,7 @@ local gears_obj = require("gears.object") local clients = {} -local client = awesome._shim_fake_class() +local client, meta = awesome._shim_fake_class() local function add_signals(c) c:add_signal("property::width") @@ -119,7 +119,10 @@ function client.gen_fake(args) client.emit_signal("manage", ret) assert(not args.screen or (args.screen == ret.screen)) - return ret + return setmetatable(ret, { + __index = function(...) return meta.__index(...) end, + __newindex = function(...) return meta.__newindex(...) end + }) end function client.get(s) diff --git a/tests/examples/shims/screen.lua b/tests/examples/shims/screen.lua index 6fe43a82..a0d4eb6b 100644 --- a/tests/examples/shims/screen.lua +++ b/tests/examples/shims/screen.lua @@ -1,6 +1,6 @@ local gears_obj = require("gears.object") -local screen = awesome._shim_fake_class() +local screen, meta = awesome._shim_fake_class() screen.count = 1 @@ -8,6 +8,7 @@ local function create_screen(args) local s = gears_obj() s:add_signal("property::workarea") + s:add_signal("property::index") s:add_signal("padding") -- Copy the geo in case the args are mutated @@ -42,8 +43,11 @@ local function create_screen(args) width = geo.width - 2*wa, height = geo.height - 2*wa, } + else + return meta.__index(_, key) end end, + __newindex = function(...) return meta.__newindex(...) end }) end diff --git a/tests/examples/shims/tag.lua b/tests/examples/shims/tag.lua index fe10da12..e8fd463e 100644 --- a/tests/examples/shims/tag.lua +++ b/tests/examples/shims/tag.lua @@ -9,8 +9,11 @@ local function new_tag(_, args) ret:add_signal("property::name") ret:add_signal("property::geometry") ret:add_signal("property::screen") + ret:add_signal("property::master_width_factor") ret:add_signal("property::mwfact") ret:add_signal("property::ncol") + ret:add_signal("property::column_count") + ret:add_signal("property::master_count") ret:add_signal("property::nmaster") ret:add_signal("property::index") ret:add_signal("property::useless_gap") @@ -35,7 +38,7 @@ local function new_tag(_, args) return setmetatable(ret, { __index = function(...) return meta.__index(...) end, - __new_index = function(...) return meta.__new_index(...) end + __newindex = function(...) return meta.__newindex(...) end }) end diff --git a/tests/test-awesomerc.lua b/tests/test-awesomerc.lua new file mode 100644 index 00000000..012fda0d --- /dev/null +++ b/tests/test-awesomerc.lua @@ -0,0 +1,236 @@ +local awful = require("awful") + +-- luacheck: globals modkey + +local old_c = nil + +local function get_callback(mod, key) + local inf = {} + awful.key(mod, key, nil, nil, inf) + + return inf.execute +end + +-- Get a tag and a client +local function get_c_and_t() + client.focus = old_c or client.get()[1] + local c = client.focus + + local t = c.screen.selected_tag + + return c, t +end + +-- display deprecated warnings +--awful.util.deprecate = function() end + +local has_spawned = false + +local steps = { + +function(count) + if count <= 1 and not has_spawned and #client.get() < 2 then + for _=1, 5 do awful.spawn("xterm") end + has_spawned = true + elseif #client.get() >= 5 then + local c, _ = get_c_and_t() + old_c = c + + return true + end +end, + +-- Wait for the focus to change +function() + if has_spawned then + has_spawned = false + return nil + end + + assert(old_c) + + -- Test layout + -- local cb = get_callback({modkey}, " ") + -- assert(cb) + + --TODO use the key once the bug is fixed + local l = old_c.screen.selected_tag.layout + assert(l) + + -- cb() + awful.layout.inc(1) + + assert(old_c.screen.selected_tag.layout ~= l) + + -- Test ontop + + assert(not old_c.ontop) + get_callback({modkey}, "t")() + + return true +end, + +-- Ok, no now ontop should be true +function() + local _, t = get_c_and_t() + + -- Give awesome some time + if not old_c.ontop then return nil end + + assert(old_c.ontop) + + -- Now, test the master_width_factor + assert(t.master_width_factor == 0.5) + + get_callback({modkey}, "l")() + + return true +end, + +-- The master width factor should now be bigger +function() + local _, t = get_c_and_t() + + assert(t.master_width_factor == 0.55) + + -- Now, test the master_count + assert(t.master_count == 1) + + get_callback({modkey, "Shift"}, "h")() + + return true +end, + +-- The number of master client should now be 2 +function() + local _, t = get_c_and_t() + + assert(t.master_count == 2) + + -- Now, test the column_count + assert(t.column_count == 1) + + get_callback({modkey, "Control"}, "h")() + get_callback({modkey, "Shift" }, "l")() + + return true +end, + +-- The number of columns should now be 2 +function() + local _, t = get_c_and_t() + + assert(t.column_count == 2) + + -- Now, test the switching tag + assert(t.index == 1) + + get_callback({modkey, }, "Right")() + + return true +end, + +-- The tag index should now be 2 +function() + local tags = mouse.screen.tags +-- local t = awful.screen.focused().selected_tag + +-- assert(t.index == 2)--FIXME + + assert(tags[1].index == 1) + tags[1]:view_only() + + return true +end, + +-- Before testing tags, lets make sure everything is still right +function() + local tags = mouse.screen.tags + + assert(tags[1].selected) + + local clients = mouse.screen.clients + + -- Make sure the clients are all on the same screen, they should be + local c_scr = client.get()[1].screen + + for _, c in ipairs(client.get()) do + assert(c_scr == c.screen) + end + + -- Then this should be true + assert(#clients == #client.get()) + + assert(#mouse.screen.all_clients == #clients) + + assert(#mouse.screen.all_clients - #mouse.screen.hidden_clients == #clients) + + return true +end, + +-- Now, test switching tags +function() + local tags = mouse.screen.tags + local clients = mouse.screen.all_clients + + assert(#tags == 9) + + assert(#clients == 5) + + assert(mouse.screen.selected_tag == tags[1]) + + for i=1, 9 do + -- Check that assertion, because if it's false, the other assert() + -- wont make any sense. + assert(tags[i].index == i) + end + + for i=1, 9 do + tags[i]:view_only() + assert(tags[i].selected) + assert(#mouse.screen.selected_tags == 1) + end + + tags[1]:view_only() + + return true +end, + +-- Lets shift some clients around +function() + local tags = mouse.screen.tags + + -- Given all tags have been selected, the selection should be back on + -- tags[1] and the client history should be kept + assert(client.focus == old_c) + + --get_callback({modkey, "Shift" }, "#"..(9+i))() --FIXME + client.focus:move_to_tag(tags[2]) + + assert(not client.focus) + + return true +end, + +-- The client should be on tag 5 +function() + -- Confirm the move did happen + local tags = mouse.screen.tags + assert(tags[1].selected) + assert(#old_c:tags() == 1) + assert(old_c:tags()[1] ~= tags[1]) + assert(not old_c:tags()[1].selected) + + -- The focus should have changed by now, as the tag isn't visible + assert(client.focus ~= old_c) + + assert(old_c:tags()[1] == tags[2]) + + assert(#tags[2]:clients() == 1) + assert(#tags[1]:clients() == 4) + + return true +end +} + +require("_runner").run_steps(steps) diff --git a/tests/test-awful-client.lua b/tests/test-awful-client.lua new file mode 100644 index 00000000..b1897d44 --- /dev/null +++ b/tests/test-awful-client.lua @@ -0,0 +1,83 @@ +local awful = require("awful") + +awful.util.deprecate = function() end + +local has_spawned = false +local steps = { + +function(count) + +if count <= 1 and not has_spawned and #client.get() < 2 then + awful.spawn("xterm") + awful.spawn("xterm") + has_spawned = true +elseif #client.get() >= 2 then + +-- Test properties +client.focus = client.get()[1] +local c = client.focus +-- local c2 = client.get()[2] + +client.add_signal("property::foo") +c.foo = "bar" + +-- Check if the property system works +assert(c.foo == "bar") +assert(c.foo == awful.client.property.get(c, "foo")) + +-- Test jumpto + +--FIXME doesn't work +-- c2:jump_to() +-- assert(client.focus == c2) +-- awful.client.jumpto(c) +-- assert(client.focus == c) + +-- Test moveresize +c.size_hints_honor = false +c:geometry {x=0,y=0,width=50,height=50} +c:relative_move( 100, 100, 50, 50 ) +for _,v in pairs(c:geometry()) do + assert(v == 100) +end +awful.client.moveresize(-25, -25, -25, -25, c ) +for _,v in pairs(c:geometry()) do + assert(v == 75) +end + +-- Test movetotag + +local t = tags[mouse.screen][1] +local t2 = tags[mouse.screen][2] + +c:tags{t} +assert(c:tags()[1] == t) +c:move_to_tag(t2) +assert(c:tags()[1] == t2) +awful.client.movetotag(t, c) +assert(c:tags()[1] == t) + +-- Test toggletag + +c:tags{t} +c:toggle_tag(t2) +assert(c:tags()[1] == t2 or c:tags()[2] == t2) +awful.client.toggletag(t2, c) +assert(c:tags()[1] == t and c:tags()[1] ~= t2 and c:tags()[2] == nil) + +-- Test movetoscreen +--FIXME I don't have the hardware to test + +-- Test floating +assert(c.floating ~= nil and type(c.floating) == "boolean") +c.floating = true +assert(awful.client.floating.get(c)) +awful.client.floating.set(c, false) +assert(not c.floating) + +return true +end +end +} + +require("_runner").run_steps(steps) diff --git a/tests/test-awful-screen.lua b/tests/test-awful-screen.lua new file mode 100644 index 00000000..47a2d43f --- /dev/null +++ b/tests/test-awful-screen.lua @@ -0,0 +1,89 @@ +local awful = require("awful") + +awful.util.deprecate = function() end + +local has_spawned = false +local steps = { + +function(count) + +if count <= 1 and not has_spawned and #client.get() < 2 then + awful.spawn("xterm") + awful.spawn("xterm") + has_spawned = true +elseif #client.get() >= 2 then + +-- Test properties +client.focus = client.get()[1] + +local s = mouse.screen + +assert(s) + +assert(s == screen[s]) + +-- Test padding + +s.padding = 42 + +local counter = 0 +for _, v in pairs(s.padding) do + assert(v == 42) + counter = counter + 1 +end + +assert(counter == 4) + +awful.screen.padding(s, { + left = 1337, + right = 1337, + top = 1337, + bottom = 1337, +}) + +counter = 0 +for _, v in pairs(s.padding) do + assert(v == 1337) + counter = counter + 1 +end + +assert(counter == 4) + +counter = 0 +for _, v in pairs(awful.screen.padding(s)) do + assert(v == 1337) + counter = counter + 1 +end + +assert(counter == 4) + +-- Test square distance + +assert(s:get_square_distance(9999, 9999)) + +assert(s:get_square_distance(9999, 9999) + == awful.screen.getdistance_sq(s, 9999, 9999)) + +-- Test count + +counter = 0 + +for _ in screen do + counter = counter + 1 +end + +assert(screen.count() == counter) + +counter = 0 +awful.screen.connect_for_each_screen(function() + counter = counter + 1 +end) + +assert(screen.count() == counter) + +return true +end +end +} + +require("_runner").run_steps(steps) diff --git a/tests/test-awful-tag.lua b/tests/test-awful-tag.lua new file mode 100644 index 00000000..076cd0b3 --- /dev/null +++ b/tests/test-awful-tag.lua @@ -0,0 +1,111 @@ +local awful = require("awful") +local beautiful = require("beautiful") + +awful.util.deprecate = function() end + +local has_spawned = false +local steps = { + +function(count) + +if count <= 1 and not has_spawned and #client.get() < 2 then + awful.spawn("xterm") + awful.spawn("xterm") + has_spawned = true +elseif #client.get() >= 2 then + +-- Test move, swap and index + +local tags = mouse.screen.tags + +assert(#mouse.screen.tags == 9) + +for k, v in ipairs(tags) do + assert(k == v.index) +end + +tags[7].index = 9 +assert(tags[7].index == 9) + +tags[7].index = 4 +assert(tags[7].index == 4) + +awful.tag.move(5, tags[7]) +assert(tags[7].index == 5) + +tags[1]:swap(tags[3]) + +assert(tags[1].index == 3) +assert(tags[3].index == 1) + +awful.tag.swap(tags[1], tags[3]) + +assert(tags[3].index == 3) +assert(tags[1].index == 1) + +-- Test add, icon and delete + +client.focus = client.get()[1] +local c = client.focus +assert(c and client.focus == c) +assert(beautiful.awesome_icon) + +local t = awful.tag.add("Test", {clients={c}, icon = beautiful.awesome_icon}) + +local found = false + +tags = mouse.screen.tags + +assert(#tags == 10) + +for _, v in ipairs(tags) do + if t == v then + found = true + break + end +end + +assert(found) + +assert(t:clients()[1] == c) +assert(c:tags()[2] == t) +assert(t.icon == beautiful.awesome_icon) + +t:delete() + +tags = mouse.screen.tags +assert(#tags == 9) + +found = false + +for _, v in ipairs(tags) do + if t == v then + found = true + break + end +end + +assert(not found) + +-- Test selected tags, view only and selected() + +t = tags[2] + +assert(not t.selected) + +assert(t.screen.selected_tag == tags[1]) + +t:view_only() + +assert(t.selected) + +assert(not tags[1].selected) + +assert(#t.screen.selected_tags == 1) + +return true +end +end +} + +require("_runner").run_steps(steps) diff --git a/tests/test-urgent.lua b/tests/test-urgent.lua index 2c471422..1f9706e2 100644 --- a/tests/test-urgent.lua +++ b/tests/test-urgent.lua @@ -32,7 +32,7 @@ local steps = { if count == 1 then -- Setup. urgent_cb_done = false -- Select first tag. - awful.tag.viewonly(tags[awful.screen.focused()][1]) + tags[awful.screen.focused()][1]:view_only() runner.add_to_default_rules({ rule = { class = "XTerm" }, properties = { tag = tags[awful.screen.focused()][2], focus = true } }) @@ -72,7 +72,7 @@ local steps = { urgent_cb_done = false -- Select first tag. - awful.tag.viewonly(tags[awful.screen.focused()][1]) + tags[awful.screen.focused()][1]:view_only() runner.add_to_default_rules({ rule = { class = "XTerm" }, properties = { tag = tags[awful.screen.focused()][2], focus = true, switchtotag = true }})