1--------------------------------------------------------------------------- 2--- Useful functions for tag manipulation. 3-- 4-- @author Julien Danjou <julien@danjou.info> 5-- @copyright 2008 Julien Danjou 6-- @module tag 7--------------------------------------------------------------------------- 8 9-- Grab environment we need 10local gdebug = require("gears.debug") 11local ascreen = require("awful.screen") 12local beautiful = require("beautiful") 13local gmath = require("gears.math") 14local object = require("gears.object") 15local timer = require("gears.timer") 16local gtable = require("gears.table") 17local alayout = nil 18local pairs = pairs 19local ipairs = ipairs 20local table = table 21local setmetatable = setmetatable 22local capi = 23{ 24 tag = tag, 25 screen = screen, 26 mouse = mouse, 27 client = client, 28 root = root 29} 30 31local function get_screen(s) 32 return s and capi.screen[s] 33end 34 35local tag = {object = {}, mt = {} } 36 37-- Private data 38local data = {} 39data.history = {} 40 41-- History functions 42tag.history = {} 43tag.history.limit = 20 44 45-- Default values 46local defaults = {} 47 48-- The gap between clients (in points). 49defaults.gap = 0 50 51-- The default gap_count. 52defaults.gap_single_client = true 53 54-- The default master fill policy. 55defaults.master_fill_policy = "expand" 56 57-- The default master width factor. 58defaults.master_width_factor = 0.5 59 60-- The default master count. 61defaults.master_count = 1 62 63-- The default column count. 64defaults.column_count = 1 65 66-- screen.tags depend on index, it cannot be used by awful.tag 67local function raw_tags(scr) 68 local tmp_tags = {} 69 for _, t in ipairs(root.tags()) do 70 if get_screen(t.screen) == scr then 71 table.insert(tmp_tags, t) 72 end 73 end 74 75 return tmp_tags 76end 77 78local function custom_layouts(self) 79 local cls = tag.getproperty(self, "_custom_layouts") 80 81 if not cls then 82 cls = {} 83 tag.setproperty(self, "_custom_layouts", cls) 84 end 85 86 return cls 87end 88 89-- Update the "user visible" list of layouts. If `from` and `to` are not the 90-- same, then `from` will be replaced. This is necessary for either the layouts 91-- defined as a function (called "template" below) and object oriented, stateful 92-- layouts where the original entry is only a constructor. 93local function update_layouts(self, from, to) 94 if not to then return end 95 96 alayout = alayout or require("awful.layout") 97 local override = tag.getproperty(self, "_layouts") 98 99 local pos = from and gtable.hasitem(override or {}, from) or nil 100 101 -- There is an override and the layout template is part of it, replace by 102 -- the instance. 103 if override and pos and from ~= to then 104 assert(type(pos) == 'number') 105 override[pos] = to 106 self:emit_signal("property::layouts") 107 return 108 end 109 110 -- Only add to the custom_layouts and preserve the ability to globally 111 -- set the layouts. 112 if override and not pos then 113 table.insert(override, to) 114 self:emit_signal("property::layouts") 115 return 116 end 117 118 pos = from and gtable.hasitem(alayout.layouts, from) or nil 119 120 local cls = custom_layouts(self) 121 122 -- The new layout is part of the global layouts. Fork the list. 123 if pos and from ~= to then 124 local cloned = gtable.clone(alayout.layouts, false) 125 cloned[pos] = to 126 gtable.merge(cloned, cls) 127 self.layouts = cloned 128 return 129 end 130 131 if pos then return end 132 133 if gtable.hasitem(cls, to) then return end 134 135 -- This layout is unknown, add it to the custom list 136 table.insert(cls, to) 137 self:emit_signal("property::layouts") 138end 139 140--- The number of elements kept in the history. 141-- @tfield integer awful.tag.history.limit 142-- @tparam[opt=20] integer limit 143 144--- The tag index. 145-- 146-- The index is the position as shown in the `awful.widget.taglist`. 147-- 148-- **Signal:** 149-- 150-- * *property::index* 151-- 152-- @property index 153-- @param integer 154-- @treturn number The tag index. 155 156function tag.object.set_index(self, idx) 157 local scr = get_screen(tag.getproperty(self, "screen")) 158 159 -- screen.tags cannot be used as it depend on index 160 local tmp_tags = raw_tags(scr) 161 162 -- sort the tags by index 163 table.sort(tmp_tags, function(a, b) 164 local ia, ib = tag.getproperty(a, "index"), tag.getproperty(b, "index") 165 return (ia or math.huge) < (ib or math.huge) 166 end) 167 168 if (not idx) or (idx < 1) or (idx > #tmp_tags) then 169 return 170 end 171 172 local rm_index = nil 173 174 for i, t in ipairs(tmp_tags) do 175 if t == self then 176 table.remove(tmp_tags, i) 177 rm_index = i 178 break 179 end 180 end 181 182 table.insert(tmp_tags, idx, self) 183 for i = idx < rm_index and idx or rm_index, #tmp_tags do 184 local tmp_tag = tmp_tags[i] 185 tag.object.set_screen(tmp_tag, scr) 186 tag.setproperty(tmp_tag, "index", i) 187 end 188end 189 190function tag.object.get_index(query_tag) 191 192 local idx = tag.getproperty(query_tag, "index") 193 194 if idx then return idx end 195 196 -- Get an unordered list of tags 197 local tags = raw_tags(query_tag.screen) 198 199 -- Too bad, lets compute it 200 for i, t in ipairs(tags) do 201 if t == query_tag then 202 tag.setproperty(t, "index", i) 203 return i 204 end 205 end 206end 207 208--- Move a tag to an absolute position in the screen[]:tags() table. 209-- @deprecated awful.tag.move 210-- @param new_index Integer absolute position in the table to insert. 211-- @param target_tag The tag that should be moved. If null, the currently 212-- selected tag is used. 213-- @see index 214function tag.move(new_index, target_tag) 215 gdebug.deprecate("Use t.index = new_index instead of awful.tag.move", {deprecated_in=4}) 216 217 target_tag = target_tag or ascreen.focused().selected_tag 218 tag.object.set_index(target_tag, new_index) 219end 220 221--- Swap 2 tags 222-- @function tag.swap 223-- @param tag2 The second tag 224-- @see client.swap 225function tag.object.swap(self, tag2) 226 local idx1, idx2 = tag.object.get_index(self), tag.object.get_index(tag2) 227 local scr2, scr1 = tag.getproperty(tag2, "screen"), tag.getproperty(self, "screen") 228 229 -- If they are on the same screen, avoid recomputing the whole table 230 -- for nothing. 231 if scr1 == scr2 then 232 tag.setproperty(self, "index", idx2) 233 tag.setproperty(tag2, "index", idx1) 234 else 235 tag.object.set_screen(tag2, scr1) 236 tag.object.set_index (tag2, idx1) 237 tag.object.set_screen(self, scr2) 238 tag.object.set_index (self, idx2) 239 end 240end 241 242--- Swap 2 tags 243-- @deprecated awful.tag.swap 244-- @see tag.swap 245-- @param tag1 The first tag 246-- @param tag2 The second tag 247function tag.swap(tag1, tag2) 248 gdebug.deprecate("Use t:swap(tag2) instead of awful.tag.swap", {deprecated_in=4}) 249 250 tag.object.swap(tag1, tag2) 251end 252 253--- Add a tag. 254-- 255-- This function allow to create tags from a set of properties: 256-- 257-- local t = awful.tag.add("my new tag", { 258-- screen = screen.primary, 259-- layout = awful.layout.suit.max, 260-- }) 261-- 262-- @function awful.tag.add 263-- @param name The tag name, a string 264-- @param props The tags inital properties, a table 265-- @return The created tag 266-- @see tag.delete 267function tag.add(name, props) 268 local properties = props or {} 269 270 -- Be sure to set the screen before the tag is activated to avoid function 271 -- connected to property::activated to be called without a valid tag. 272 -- set properties cannot be used as this has to be set before the first 273 -- signal is sent 274 properties.screen = get_screen(properties.screen or ascreen.focused()) 275 -- Index is also required 276 properties.index = properties.index or #raw_tags(properties.screen)+1 277 278 local newtag = capi.tag{ name = name } 279 280 -- Start with a fresh property table to avoid collisions with unsupported data 281 newtag.data.awful_tag_properties = {screen=properties.screen, index=properties.index} 282 283 newtag.activated = true 284 285 for k, v in pairs(properties) do 286 -- `rawget` doesn't work on userdata, `:clients()` is the only relevant 287 -- entry. 288 if k == "clients" or tag.object[k] then 289 newtag[k](newtag, v) 290 else 291 newtag[k] = v 292 end 293 end 294 295 return newtag 296end 297 298--- Create a set of tags and attach it to a screen. 299-- @function awful.tag.new 300-- @param names The tag name, in a table 301-- @param screen The tag screen, or 1 if not set. 302-- @param layout The layout or layout table to set for this tags by default. 303-- @return A table with all created tags. 304function tag.new(names, screen, layout) 305 screen = get_screen(screen or 1) 306 -- True if `layout` should be used as the layout of each created tag 307 local have_single_layout = (not layout) or (layout.arrange and layout.name) 308 local tags = {} 309 for id, name in ipairs(names) do 310 local l = layout 311 if not have_single_layout then 312 l = layout[id] or layout[1] 313 end 314 table.insert(tags, id, tag.add(name, {screen = screen, layout = l})) 315 -- Select the first tag. 316 if id == 1 then 317 tags[id].selected = true 318 end 319 end 320 321 return tags 322end 323 324--- Find a suitable fallback tag. 325-- @function awful.tag.find_fallback 326-- @param screen The screen to look for a tag on. [awful.screen.focused()] 327-- @param invalids A table of tags we consider unacceptable. [selectedlist(scr)] 328function tag.find_fallback(screen, invalids) 329 local scr = screen or ascreen.focused() 330 local t = invalids or scr.selected_tags 331 332 for _, v in pairs(scr.tags) do 333 if not gtable.hasitem(t, v) then return v end 334 end 335end 336 337--- Delete a tag. 338-- 339-- To delete the current tag: 340-- 341-- mouse.screen.selected_tag:delete() 342-- 343-- @function tag.delete 344-- @see awful.tag.add 345-- @see awful.tag.find_fallback 346-- @tparam[opt=awful.tag.find_fallback()] tag fallback_tag Tag to assign 347-- stickied tags to. 348-- @tparam[opt=false] boolean force Move even non-sticky clients to the fallback 349-- tag. 350-- @return Returns true if the tag is successfully deleted. 351-- If there are no clients exclusively on this tag then delete it. Any 352-- stickied clients are assigned to the optional 'fallback_tag'. 353-- If after deleting the tag there is no selected tag, try and restore from 354-- history or select the first tag on the screen. 355function tag.object.delete(self, fallback_tag, force) 356 -- abort if the taf isn't currently activated 357 if not self.activated then return false end 358 359 local target_scr = get_screen(tag.getproperty(self, "screen")) 360 local tags = target_scr.tags 361 local idx = tag.object.get_index(self) 362 local ntags = #tags 363 364 -- We can't use the target tag as a fallback. 365 if fallback_tag == self then return false end 366 367 -- No fallback_tag provided, try and get one. 368 if fallback_tag == nil then 369 fallback_tag = tag.find_fallback(target_scr, {self}) 370 end 371 372 -- Abort if we would have un-tagged clients. 373 local clients = self:clients() 374 if #clients > 0 and fallback_tag == nil then return false end 375 376 -- Move the clients we can off of this tag. 377 for _, c in pairs(clients) do 378 local nb_tags = #c:tags() 379 380 -- If a client has only this tag, or stickied clients with 381 -- nowhere to go, abort. 382 if (not c.sticky and nb_tags == 1 and not force) then 383 return 384 -- If a client has multiple tags, then do not move it to fallback 385 elseif nb_tags < 2 then 386 c:tags({fallback_tag}) 387 end 388 end 389 390 -- delete the tag 391 self.data.awful_tag_properties.screen = nil 392 self.activated = false 393 394 -- Update all indexes 395 for i=idx+1, #tags do 396 tag.setproperty(tags[i], "index", i-1) 397 end 398 399 -- If no tags are visible (and we did not delete the lasttag), try and 400 -- view one. The > 1 is because ntags is no longer synchronized with the 401 -- current count. 402 if target_scr.selected_tag == nil and ntags > 1 then 403 tag.history.restore(target_scr, 1) 404 if target_scr.selected_tag == nil then 405 local other_tag = tags[tags[1] == self and 2 or 1] 406 if other_tag then 407 other_tag.selected = true 408 end 409 end 410 end 411 412 return true 413end 414 415--- Delete a tag. 416-- @deprecated awful.tag.delete 417-- @see tag.delete 418-- @param target_tag Optional tag object to delete. [selected()] 419-- @param fallback_tag Tag to assign stickied tags to. [~selected()] 420-- @return Returns true if the tag is successfully deleted, nil otherwise. 421-- If there are no clients exclusively on this tag then delete it. Any 422-- stickied clients are assigned to the optional 'fallback_tag'. 423-- If after deleting the tag there is no selected tag, try and restore from 424-- history or select the first tag on the screen. 425function tag.delete(target_tag, fallback_tag) 426 gdebug.deprecate("Use t:delete(fallback_tag) instead of awful.tag.delete", {deprecated_in=4}) 427 428 return tag.object.delete(target_tag, fallback_tag) 429end 430 431--- Update the tag history. 432-- @function awful.tag.history.update 433-- @param obj Screen object. 434function tag.history.update(obj) 435 local s = get_screen(obj) 436 local curtags = s.selected_tags 437 -- create history table 438 if not data.history[s] then 439 data.history[s] = {} 440 else 441 if data.history[s].current then 442 -- Check that the list is not identical 443 local identical = #data.history[s].current == #curtags 444 if identical then 445 for idx, _tag in ipairs(data.history[s].current) do 446 if curtags[idx] ~= _tag then 447 identical = false 448 break 449 end 450 end 451 end 452 453 -- Do not update history the table are identical 454 if identical then return end 455 end 456 457 -- Limit history 458 if #data.history[s] >= tag.history.limit then 459 for i = tag.history.limit, #data.history[s] do 460 data.history[s][i] = nil 461 end 462 end 463 end 464 465 -- store previously selected tags in the history table 466 table.insert(data.history[s], 1, data.history[s].current) 467 data.history[s].previous = data.history[s][1] 468 -- store currently selected tags 469 data.history[s].current = setmetatable(curtags, { __mode = 'v' }) 470end 471 472--- Revert tag history. 473-- @function awful.tag.history.restore 474-- @param screen The screen. 475-- @param idx Index in history. Defaults to "previous" which is a special index 476-- toggling between last two selected sets of tags. Number (eg 1) will go back 477-- to the given index in history. 478function tag.history.restore(screen, idx) 479 local s = get_screen(screen or ascreen.focused()) 480 local i = idx or "previous" 481 local sel = s.selected_tags 482 -- do nothing if history empty 483 if not data.history[s] or not data.history[s][i] then return end 484 -- if all tags been deleted, try next entry 485 if #data.history[s][i] == 0 then 486 if i == "previous" then i = 0 end 487 tag.history.restore(s, i + 1) 488 return 489 end 490 -- deselect all tags 491 tag.viewnone(s) 492 -- select tags from the history entry 493 for _, t in ipairs(data.history[s][i]) do 494 if t.activated and t.screen then 495 t.selected = true 496 end 497 end 498 -- update currently selected tags table 499 data.history[s].current = data.history[s][i] 500 -- store previously selected tags 501 data.history[s].previous = setmetatable(sel, { __mode = 'v' }) 502 -- remove the reverted history entry 503 if i ~= "previous" then table.remove(data.history[s], i) end 504 505 s:emit_signal("tag::history::update") 506end 507 508--- Get a list of all tags on a screen 509-- @deprecated awful.tag.gettags 510-- @tparam screen s Screen 511-- @return A table with all available tags 512-- @see screen.tags 513function tag.gettags(s) 514 gdebug.deprecate("Use s.tags instead of awful.tag.gettags", {deprecated_in=4}) 515 516 s = get_screen(s) 517 518 return s and s.tags or {} 519end 520 521--- Find a tag by name. 522-- @tparam screen s The screen of the tag 523-- @tparam string name The name of the tag 524-- @return The tag found, or `nil` 525-- @usage -- For the current screen 526-- local t = awful.tag.find_by_name(awful.screen.focused(), "name") 527-- 528-- -- For a screen index 529-- local t = awful.tag.find_by_name(screen[1], "name") 530-- 531-- -- For all screens 532-- local t = awful.tag.find_by_name(nil, "name") 533function tag.find_by_name(s, name) 534 --TODO v5: swap the arguments and make screen [opt] 535 local tags = s and s.tags or root.tags() 536 for _, t in ipairs(tags) do 537 if name == t.name then 538 return t 539 end 540 end 541end 542 543--- The tag screen. 544-- 545-- **Signal:** 546-- 547-- * *property::screen* 548-- 549-- @property screen 550-- @param screen 551-- @see screen 552 553function tag.object.set_screen(t, s) 554 555 s = get_screen(s or ascreen.focused()) 556 local sel = t.selected 557 local old_screen = get_screen(tag.getproperty(t, "screen")) 558 559 if s == old_screen then return end 560 561 -- Keeping the old index make very little sense when changing screen 562 tag.setproperty(t, "index", nil) 563 564 -- Change the screen 565 tag.setproperty(t, "screen", s) 566 if s then 567 tag.setproperty(t, "index", #s:get_tags(true)) 568 end 569 570 -- Make sure the client's screen matches its tags 571 for _,c in ipairs(t:clients()) do 572 c.screen = s --Move all clients 573 c:tags({t}) 574 end 575 576 if old_screen then 577 -- Update all indexes 578 for i,t2 in ipairs(old_screen.tags) do 579 tag.setproperty(t2, "index", i) 580 end 581 582 -- Restore the old screen history if the tag was selected 583 if sel then 584 tag.history.restore(old_screen, 1) 585 end 586 end 587end 588 589--- Set a tag's screen 590-- @deprecated awful.tag.setscreen 591-- @see screen 592-- @param s Screen 593-- @param t tag object 594function tag.setscreen(s, t) 595 -- For API consistency, the arguments have been swapped for Awesome 3.6 596 -- this method is already deprecated, so be silent and swap the args 597 if type(t) == "number" then 598 s, t = t, s 599 end 600 601 gdebug.deprecate("Use t.screen = s instead of awful.tag.setscreen(t, s)", {deprecated_in=4}) 602 603 tag.object.set_screen(t, s) 604end 605 606--- Get a tag's screen 607-- @deprecated awful.tag.getscreen 608-- @see screen 609-- @param[opt] t tag object 610-- @return Screen number 611function tag.getscreen(t) 612 gdebug.deprecate("Use t.screen instead of awful.tag.getscreen(t)", {deprecated_in=4}) 613 614 -- A new getter is not required 615 616 t = t or ascreen.focused().selected_tag 617 local prop = tag.getproperty(t, "screen") 618 return prop and prop.index 619end 620 621--- Return a table with all visible tags 622-- @deprecated awful.tag.selectedlist 623-- @param s Screen. 624-- @return A table with all selected tags. 625-- @see screen.selected_tags 626function tag.selectedlist(s) 627 gdebug.deprecate("Use s.selected_tags instead of awful.tag.selectedlist", {deprecated_in=4}) 628 629 s = get_screen(s or ascreen.focused()) 630 631 return s.selected_tags 632end 633 634--- Return only the first visible tag. 635-- @deprecated awful.tag.selected 636-- @param s Screen. 637-- @see screen.selected_tag 638function tag.selected(s) 639 gdebug.deprecate("Use s.selected_tag instead of awful.tag.selected", {deprecated_in=4}) 640 641 s = get_screen(s or ascreen.focused()) 642 643 return s.selected_tag 644end 645 646--- The default master width factor 647-- 648-- @beautiful beautiful.master_width_factor 649-- @param number (default: 0.5) 650-- @see master_width_factor 651-- @see gap 652 653--- The tag master width factor. 654-- 655-- The master width factor is one of the 5 main properties used to configure 656-- the `layout`. Each layout interpret (or ignore) this property differenly. 657-- 658-- See the layout suit documentation for information about how the master width 659-- factor is used. 660-- 661-- **Signal:** 662-- 663-- * *property::mwfact* (deprecated) 664-- * *property::master_width_factor* 665-- 666-- @property master_width_factor 667-- @param number Between 0 and 1 668-- @see master_count 669-- @see column_count 670-- @see master_fill_policy 671-- @see gap 672 673function tag.object.set_master_width_factor(t, mwfact) 674 if mwfact >= 0 and mwfact <= 1 then 675 tag.setproperty(t, "mwfact", mwfact) 676 tag.setproperty(t, "master_width_factor", mwfact) 677 end 678end 679 680function tag.object.get_master_width_factor(t) 681 return tag.getproperty(t, "master_width_factor") 682 or beautiful.master_width_factor 683 or defaults.master_width_factor 684end 685 686--- Set master width factor. 687-- @deprecated awful.tag.setmwfact 688-- @see master_fill_policy 689-- @see master_width_factor 690-- @param mwfact Master width factor. 691-- @param t The tag to modify, if null tag.selected() is used. 692function tag.setmwfact(mwfact, t) 693 gdebug.deprecate("Use t.master_width_factor = mwfact instead of awful.tag.setmwfact", {deprecated_in=4}) 694 695 tag.object.set_master_width_factor(t or ascreen.focused().selected_tag, mwfact) 696end 697 698--- Increase master width factor. 699-- @function awful.tag.incmwfact 700-- @see master_width_factor 701-- @param add Value to add to master width factor. 702-- @param t The tag to modify, if null tag.selected() is used. 703function tag.incmwfact(add, t) 704 t = t or t or ascreen.focused().selected_tag 705 tag.object.set_master_width_factor(t, tag.object.get_master_width_factor(t) + add) 706end 707 708--- Get master width factor. 709-- @deprecated awful.tag.getmwfact 710-- @see master_width_factor 711-- @see master_fill_policy 712-- @param[opt] t The tag. 713function tag.getmwfact(t) 714 gdebug.deprecate("Use t.master_width_factor instead of awful.tag.getmwfact", {deprecated_in=4}) 715 716 return tag.object.get_master_width_factor(t or ascreen.focused().selected_tag) 717end 718 719--- An ordered list of layouts. 720-- `awful.tag.layout` Is usually defined in `rc.lua`. It store the list of 721-- layouts used when selecting the previous and next layouts. This is the 722-- default: 723-- 724-- -- Table of layouts to cover with awful.layout.inc, order matters. 725-- awful.layout.layouts = { 726-- awful.layout.suit.floating, 727-- awful.layout.suit.tile, 728-- awful.layout.suit.tile.left, 729-- awful.layout.suit.tile.bottom, 730-- awful.layout.suit.tile.top, 731-- awful.layout.suit.fair, 732-- awful.layout.suit.fair.horizontal, 733-- awful.layout.suit.spiral, 734-- awful.layout.suit.spiral.dwindle, 735-- awful.layout.suit.max, 736-- awful.layout.suit.max.fullscreen, 737-- awful.layout.suit.magnifier, 738-- awful.layout.suit.corner.nw, 739-- -- awful.layout.suit.corner.ne, 740-- -- awful.layout.suit.corner.sw, 741-- -- awful.layout.suit.corner.se, 742-- } 743-- 744-- @field awful.tag.layouts 745 746--- The tag client layout. 747-- 748-- This property hold the layout. A layout can be either stateless or stateful. 749-- Stateless layouts are used by default by Awesome. They tile clients without 750-- any other overhead. They take an ordered list of clients and place them on 751-- the screen. Stateful layouts create an object instance for each tags and 752-- can store variables and metadata. Because of this, they are able to change 753-- over time and be serialized (saved). 754-- 755-- Both types of layouts have valid usage scenarios. 756-- 757-- **Stateless layouts:** 758-- 759-- These layouts are stored in `awful.layout.suit`. They expose a table with 2 760-- fields: 761-- 762-- * **name** (*string*): The layout name. This should be unique. 763-- * **arrange** (*function*): The function called when the clients need to be 764-- placed. The only parameter is a table or arguments returned by 765-- `awful.layout.parameters` 766-- 767-- **Stateful layouts:** 768-- 769-- The stateful layouts API is the same as stateless, but they are a function 770-- returining a layout instead of a layout itself. They also should have an 771-- `is_dynamic = true` property. If they don't, `awful.tag` will create a new 772-- instance everytime the layout is set. If they do, the instance will be 773-- cached and re-used. 774-- 775-- **Signal:** 776-- 777-- * *property::layout* 778-- 779-- @property layout 780-- @see awful.tag.layouts 781-- @tparam layout|function layout A layout table or a constructor function 782-- @return The layout 783 784--- The (proposed) list of available layouts for this tag. 785-- 786-- This property allows to define a subset (or superset) of layouts available 787-- in the "rotation table". In the default configuration file, `Mod4+Space` 788-- and `Mod4+Shift+Space` are used to switch between tags. The 789-- `awful.widget.layoutlist` also uses this as its default layout filter. 790-- 791-- By default, it will be the same as `awful.layout.layouts` unless there the 792-- a layout not present is used. If that's the case they will be added at the 793-- front of the list. 794-- 795-- @property layouts 796-- @param table 797-- @see awful.layout.layouts 798-- @see layout 799 800function tag.object.set_layout(t, layout) 801 802 local template = nil 803 804 -- Check if the signature match a stateful layout 805 if type(layout) == "function" or ( 806 type(layout) == "table" 807 and getmetatable(layout) 808 and getmetatable(layout).__call 809 ) then 810 if not t.dynamic_layout_cache then 811 t.dynamic_layout_cache = {} 812 end 813 814 local instance = t.dynamic_layout_cache[layout] or layout(t) 815 816 -- Always make sure the layout is notified it is enabled 817 if tag.getproperty(t, "screen").selected_tag == t and instance.wake_up then 818 instance:wake_up() 819 end 820 821 -- Avoid creating the same layout twice, use layout:reset() to reset 822 if instance.is_dynamic then 823 t.dynamic_layout_cache[layout] = instance 824 end 825 826 template = layout 827 layout = instance 828 end 829 830 tag.setproperty(t, "layout", layout) 831 832 update_layouts(t, template or layout, layout) 833 834 return layout 835end 836 837function tag.object.get_layouts(self) 838 local override = tag.getproperty(self, "_layouts") 839 840 if override then 841 return override 842 end 843 844 -- Required to get the default/fallback list of layouts 845 alayout = alayout or require("awful.layout") 846 847 local cls = custom_layouts(self) 848 849 -- Without the clone, the custom_layouts would grow 850 return #cls > 0 and gtable.merge(gtable.clone(cls, false), alayout.layouts) or 851 alayout.layouts 852end 853 854function tag.object.set_layouts(self, layouts) 855 tag.setproperty(self, "_custom_layouts", {}) 856 tag.setproperty(self, "_layouts", gtable.clone(layouts, false)) 857 858 local cur = tag.getproperty(self, "layout") 859 update_layouts(self, cur, cur) 860 861 self:emit_signal("property::layouts") 862end 863 864function tag.object.get_layout(t) 865 local l = tag.getproperty(t, "layout") 866 if l then return l end 867 868 local layouts = tag.getproperty(t, "_layouts") 869 870 return layouts and layouts[1] 871 or require("awful.layout.suit.floating") 872end 873 874--- Set layout. 875-- @deprecated awful.tag.setlayout 876-- @see layout 877-- @param layout a layout table or a constructor function 878-- @param t The tag to modify 879-- @return The layout 880function tag.setlayout(layout, t) 881 gdebug.deprecate("Use t.layout = layout instead of awful.tag.setlayout", {deprecated_in=4}) 882 883 return tag.object.set_layout(t, layout) 884end 885 886--- Define if the tag must be deleted when the last client is untagged. 887-- 888-- This is useful to create "throw-away" tags for operation like 50/50 889-- side-by-side views. 890-- 891-- local t = awful.tag.add("Temporary", { 892-- screen = client.focus.screen, 893-- volatile = true, 894-- clients = { 895-- client.focus, 896-- awful.client.focus.history.get(client.focus.screen, 1) 897-- } 898-- } 899-- 900-- **Signal:** 901-- 902-- * *property::volatile* 903-- 904-- @property volatile 905-- @param boolean 906 907-- Volatile accessors are implicit 908 909--- Set if the tag must be deleted when the last client is untagged 910-- @deprecated awful.tag.setvolatile 911-- @see volatile 912-- @tparam boolean volatile If the tag must be deleted when the last client is untagged 913-- @param t The tag to modify, if null tag.selected() is used. 914function tag.setvolatile(volatile, t) 915 gdebug.deprecate("Use t.volatile = volatile instead of awful.tag.setvolatile", {deprecated_in=4}) 916 917 tag.setproperty(t, "volatile", volatile) 918end 919 920--- Get if the tag must be deleted when the last client closes 921-- @deprecated awful.tag.getvolatile 922-- @see volatile 923-- @param t The tag to modify, if null tag.selected() is used. 924-- @treturn boolean If the tag will be deleted when the last client is untagged 925function tag.getvolatile(t) 926 gdebug.deprecate("Use t.volatile instead of awful.tag.getvolatile", {deprecated_in=4}) 927 928 return tag.getproperty(t, "volatile") or false 929end 930 931--- The default gap. 932-- 933-- @beautiful beautiful.useless_gap 934-- @param number (default: 0) 935-- @see gap 936-- @see gap_single_client 937 938--- The gap (spacing, also called `useless_gap`) between clients. 939-- 940-- This property allow to waste space on the screen in the name of style, 941-- unicorns and readability. 942-- 943-- **Signal:** 944-- 945-- * *property::useless_gap* 946-- 947-- @property gap 948-- @param number The value has to be greater than zero. 949-- @see gap_single_client 950 951function tag.object.set_gap(t, useless_gap) 952 if useless_gap >= 0 then 953 tag.setproperty(t, "useless_gap", useless_gap) 954 end 955end 956 957function tag.object.get_gap(t) 958 return tag.getproperty(t, "useless_gap") 959 or beautiful.useless_gap 960 or defaults.gap 961end 962 963--- Set the spacing between clients 964-- @deprecated awful.tag.setgap 965-- @see gap 966-- @param useless_gap The spacing between clients 967-- @param t The tag to modify, if null tag.selected() is used. 968function tag.setgap(useless_gap, t) 969 gdebug.deprecate("Use t.gap = useless_gap instead of awful.tag.setgap", {deprecated_in=4}) 970 971 tag.object.set_gap(t or ascreen.focused().selected_tag, useless_gap) 972end 973 974--- Increase the spacing between clients 975-- @function awful.tag.incgap 976-- @see gap 977-- @param add Value to add to the spacing between clients 978-- @param t The tag to modify, if null tag.selected() is used. 979function tag.incgap(add, t) 980 t = t or t or ascreen.focused().selected_tag 981 tag.object.set_gap(t, tag.object.get_gap(t) + add) 982end 983 984--- Enable gaps for a single client. 985-- 986-- @beautiful beautiful.gap_single_client 987-- @param boolean (default: true) 988-- @see gap 989-- @see gap_single_client 990 991--- Enable gaps for a single client. 992-- 993-- **Signal:** 994-- 995-- * *property::gap\_single\_client* 996-- 997-- @property gap_single_client 998-- @param boolean Enable gaps for a single client 999 1000function tag.object.set_gap_single_client(t, gap_single_client) 1001 tag.setproperty(t, "gap_single_client", gap_single_client == true) 1002end 1003 1004function tag.object.get_gap_single_client(t) 1005 local val = tag.getproperty(t, "gap_single_client") 1006 if val ~= nil then 1007 return val 1008 end 1009 val = beautiful.gap_single_client 1010 if val ~= nil then 1011 return val 1012 end 1013 return defaults.gap_single_client 1014end 1015 1016--- Get the spacing between clients. 1017-- @deprecated awful.tag.getgap 1018-- @see gap 1019-- @tparam[opt=tag.selected()] tag t The tag. 1020-- @tparam[opt] int numclients Number of (tiled) clients. Passing this will 1021-- return 0 for a single client. You can override this function to change 1022-- this behavior. 1023function tag.getgap(t, numclients) 1024 gdebug.deprecate("Use t.gap instead of awful.tag.getgap", {deprecated_in=4}) 1025 1026 if numclients == 1 then 1027 return 0 1028 end 1029 1030 return tag.object.get_gap(t or ascreen.focused().selected_tag) 1031end 1032 1033--- The default fill policy. 1034-- 1035-- ** Possible values**: 1036-- 1037-- * *expand*: Take all the space 1038-- * *master_width_factor*: Only take the ratio defined by the 1039-- `master_width_factor` 1040-- 1041-- @beautiful beautiful.master_fill_policy 1042-- @param string (default: "expand") 1043-- @see master_fill_policy 1044 1045--- Set size fill policy for the master client(s). 1046-- 1047-- ** Possible values**: 1048-- 1049-- * *expand*: Take all the space 1050-- * *master_width_factor*: Only take the ratio defined by the 1051-- `master_width_factor` 1052-- 1053-- **Signal:** 1054-- 1055-- * *property::master_fill_policy* 1056-- 1057-- @property master_fill_policy 1058-- @param string "expand" or "master_width_factor" 1059 1060function tag.object.get_master_fill_policy(t) 1061 return tag.getproperty(t, "master_fill_policy") 1062 or beautiful.master_fill_policy 1063 or defaults.master_fill_policy 1064end 1065 1066--- Set size fill policy for the master client(s) 1067-- @deprecated awful.tag.setmfpol 1068-- @see master_fill_policy 1069-- @tparam string policy Can be set to 1070-- "expand" (fill all the available workarea) or 1071-- "master_width_factor" (fill only an area inside the master width factor) 1072-- @tparam[opt=tag.selected()] tag t The tag to modify 1073function tag.setmfpol(policy, t) 1074 gdebug.deprecate("Use t.master_fill_policy = policy instead of awful.tag.setmfpol", {deprecated_in=4}) 1075 1076 t = t or ascreen.focused().selected_tag 1077 tag.setproperty(t, "master_fill_policy", policy) 1078end 1079 1080--- Toggle size fill policy for the master client(s) 1081-- between "expand" and "master_width_factor". 1082-- @function awful.tag.togglemfpol 1083-- @see master_fill_policy 1084-- @tparam tag t The tag to modify, if null tag.selected() is used. 1085function tag.togglemfpol(t) 1086 t = t or ascreen.focused().selected_tag 1087 1088 if tag.getmfpol(t) == "expand" then 1089 tag.setproperty(t, "master_fill_policy", "master_width_factor") 1090 else 1091 tag.setproperty(t, "master_fill_policy", "expand") 1092 end 1093end 1094 1095--- Get size fill policy for the master client(s) 1096-- @deprecated awful.tag.getmfpol 1097-- @see master_fill_policy 1098-- @tparam[opt=tag.selected()] tag t The tag 1099-- @treturn string Possible values are 1100-- "expand" (fill all the available workarea, default one) or 1101-- "master_width_factor" (fill only an area inside the master width factor) 1102function tag.getmfpol(t) 1103 gdebug.deprecate("Use t.master_fill_policy instead of awful.tag.getmfpol", {deprecated_in=4}) 1104 1105 t = t or ascreen.focused().selected_tag 1106 return tag.getproperty(t, "master_fill_policy") 1107 or beautiful.master_fill_policy 1108 or defaults.master_fill_policy 1109end 1110 1111--- The default number of master windows. 1112-- 1113-- @beautiful beautiful.master_count 1114-- @param integer (default: 1) 1115-- @see master_count 1116 1117--- Set the number of master windows. 1118-- 1119-- **Signal:** 1120-- 1121-- * *property::nmaster* (deprecated) 1122-- * *property::master_count* 1123-- 1124-- @property master_count 1125-- @param integer nmaster Only positive values are accepted 1126 1127function tag.object.set_master_count(t, nmaster) 1128 if nmaster >= 0 then 1129 tag.setproperty(t, "nmaster", nmaster) 1130 tag.setproperty(t, "master_count", nmaster) 1131 end 1132end 1133 1134function tag.object.get_master_count(t) 1135 return tag.getproperty(t, "master_count") 1136 or beautiful.master_count 1137 or defaults.master_count 1138end 1139 1140--- 1141-- @deprecated awful.tag.setnmaster 1142-- @see master_count 1143-- @param nmaster The number of master windows. 1144-- @param[opt] t The tag. 1145function tag.setnmaster(nmaster, t) 1146 gdebug.deprecate("Use t.master_count = nmaster instead of awful.tag.setnmaster", {deprecated_in=4}) 1147 1148 tag.object.set_master_count(t or ascreen.focused().selected_tag, nmaster) 1149end 1150 1151--- Get the number of master windows. 1152-- @deprecated awful.tag.getnmaster 1153-- @see master_count 1154-- @param[opt] t The tag. 1155function tag.getnmaster(t) 1156 gdebug.deprecate("Use t.master_count instead of awful.tag.setnmaster", {deprecated_in=4}) 1157 1158 t = t or ascreen.focused().selected_tag 1159 return tag.getproperty(t, "master_count") or 1 1160end 1161 1162--- Increase the number of master windows. 1163-- @function awful.tag.incnmaster 1164-- @see master_count 1165-- @param add Value to add to number of master windows. 1166-- @param[opt] t The tag to modify, if null tag.selected() is used. 1167-- @tparam[opt=false] boolean sensible Limit nmaster based on the number of 1168-- visible tiled windows? 1169function tag.incnmaster(add, t, sensible) 1170 t = t or ascreen.focused().selected_tag 1171 1172 if sensible then 1173 local screen = get_screen(tag.getproperty(t, "screen")) 1174 local ntiled = #screen.tiled_clients 1175 1176 local nmaster = tag.object.get_master_count(t) 1177 if nmaster > ntiled then 1178 nmaster = ntiled 1179 end 1180 1181 local newnmaster = nmaster + add 1182 if newnmaster > ntiled then 1183 newnmaster = ntiled 1184 end 1185 tag.object.set_master_count(t, newnmaster) 1186 else 1187 tag.object.set_master_count(t, tag.object.get_master_count(t) + add) 1188 end 1189end 1190 1191--- Set the tag icon. 1192-- 1193-- **Signal:** 1194-- 1195-- * *property::icon* 1196-- 1197-- @property icon 1198-- @tparam path|surface icon The icon 1199 1200-- accessors are implicit. 1201 1202--- Set the tag icon 1203-- @deprecated awful.tag.seticon 1204-- @see icon 1205-- @param icon the icon to set, either path or image object 1206-- @param _tag the tag 1207function tag.seticon(icon, _tag) 1208 gdebug.deprecate("Use t.icon = icon instead of awful.tag.seticon", {deprecated_in=4}) 1209 1210 _tag = _tag or ascreen.focused().selected_tag 1211 tag.setproperty(_tag, "icon", icon) 1212end 1213 1214--- Get the tag icon 1215-- @deprecated awful.tag.geticon 1216-- @see icon 1217-- @param _tag the tag 1218function tag.geticon(_tag) 1219 gdebug.deprecate("Use t.icon instead of awful.tag.geticon", {deprecated_in=4}) 1220 1221 _tag = _tag or ascreen.focused().selected_tag 1222 return tag.getproperty(_tag, "icon") 1223end 1224 1225--- The default number of columns. 1226-- 1227-- @beautiful beautiful.column_count 1228-- @param integer (default: 1) 1229-- @see column_count 1230 1231--- Set the number of columns. 1232-- 1233-- **Signal:** 1234-- 1235-- * *property::ncol* (deprecated) 1236-- * *property::column_count* 1237-- 1238-- @property column_count 1239-- @tparam integer ncol Has to be greater than 1 1240 1241function tag.object.set_column_count(t, ncol) 1242 if ncol >= 1 then 1243 tag.setproperty(t, "ncol", ncol) 1244 tag.setproperty(t, "column_count", ncol) 1245 end 1246end 1247 1248function tag.object.get_column_count(t) 1249 return tag.getproperty(t, "column_count") 1250 or beautiful.column_count 1251 or defaults.column_count 1252end 1253 1254--- Set number of column windows. 1255-- @deprecated awful.tag.setncol 1256-- @see column_count 1257-- @param ncol The number of column. 1258-- @param t The tag to modify, if null tag.selected() is used. 1259function tag.setncol(ncol, t) 1260 gdebug.deprecate("Use t.column_count = new_index instead of awful.tag.setncol", {deprecated_in=4}) 1261 1262 t = t or ascreen.focused().selected_tag 1263 if ncol >= 1 then 1264 tag.setproperty(t, "ncol", ncol) 1265 tag.setproperty(t, "column_count", ncol) 1266 end 1267end 1268 1269--- Get number of column windows. 1270-- @deprecated awful.tag.getncol 1271-- @see column_count 1272-- @param[opt] t The tag. 1273function tag.getncol(t) 1274 gdebug.deprecate("Use t.column_count instead of awful.tag.getncol", {deprecated_in=4}) 1275 1276 t = t or ascreen.focused().selected_tag 1277 return tag.getproperty(t, "column_count") or 1 1278end 1279 1280--- Increase number of column windows. 1281-- @function awful.tag.incncol 1282-- @param add Value to add to number of column windows. 1283-- @param[opt] t The tag to modify, if null tag.selected() is used. 1284-- @tparam[opt=false] boolean sensible Limit column_count based on the number 1285-- of visible tiled windows? 1286function tag.incncol(add, t, sensible) 1287 t = t or ascreen.focused().selected_tag 1288 1289 if sensible then 1290 local screen = get_screen(tag.getproperty(t, "screen")) 1291 local ntiled = #screen.tiled_clients 1292 local nmaster = tag.object.get_master_count(t) 1293 local nsecondary = ntiled - nmaster 1294 1295 local ncol = tag.object.get_column_count(t) 1296 if ncol > nsecondary then 1297 ncol = nsecondary 1298 end 1299 1300 local newncol = ncol + add 1301 if newncol > nsecondary then 1302 newncol = nsecondary 1303 end 1304 1305 tag.object.set_column_count(t, newncol) 1306 else 1307 tag.object.set_column_count(t, tag.object.get_column_count(t) + add) 1308 end 1309end 1310 1311--- View no tag. 1312-- @function awful.tag.viewnone 1313-- @tparam[opt] int|screen screen The screen. 1314function tag.viewnone(screen) 1315 screen = screen or ascreen.focused() 1316 local tags = screen.tags 1317 for _, t in pairs(tags) do 1318 t.selected = false 1319 end 1320end 1321 1322--- View a tag by its taglist index. 1323-- 1324-- This is equivalent to `screen.tags[i]:view_only()` 1325-- @function awful.tag.viewidx 1326-- @see screen.tags 1327-- @param i The **relative** index to see. 1328-- @param[opt] screen The screen. 1329function tag.viewidx(i, screen) 1330 screen = get_screen(screen or ascreen.focused()) 1331 local tags = screen.tags 1332 local showntags = {} 1333 for _, t in ipairs(tags) do 1334 if not tag.getproperty(t, "hide") then 1335 table.insert(showntags, t) 1336 end 1337 end 1338 local sel = screen.selected_tag 1339 tag.viewnone(screen) 1340 for k, t in ipairs(showntags) do 1341 if t == sel then 1342 showntags[gmath.cycle(#showntags, k + i)].selected = true 1343 end 1344 end 1345 screen:emit_signal("tag::history::update") 1346end 1347 1348--- Get a tag's index in the gettags() table. 1349-- @deprecated awful.tag.getidx 1350-- @see index 1351-- @param query_tag The tag object to find. [selected()] 1352-- @return The index of the tag, nil if the tag is not found. 1353function tag.getidx(query_tag) 1354 gdebug.deprecate("Use t.index instead of awful.tag.getidx", {deprecated_in=4}) 1355 1356 return tag.object.get_index(query_tag or ascreen.focused().selected_tag) 1357end 1358 1359--- View next tag. This is the same as tag.viewidx(1). 1360-- @function awful.tag.viewnext 1361-- @param screen The screen. 1362function tag.viewnext(screen) 1363 return tag.viewidx(1, screen) 1364end 1365 1366--- View previous tag. This is the same a tag.viewidx(-1). 1367-- @function awful.tag.viewprev 1368-- @param screen The screen. 1369function tag.viewprev(screen) 1370 return tag.viewidx(-1, screen) 1371end 1372 1373--- View only a tag. 1374-- @function tag.view_only 1375-- @see selected 1376function tag.object.view_only(self) 1377 local tags = self.screen.tags 1378 -- First, untag everyone except the viewed tag. 1379 for _, _tag in pairs(tags) do 1380 if _tag ~= self then 1381 _tag.selected = false 1382 end 1383 end 1384 -- Then, set this one to selected. 1385 -- We need to do that in 2 operations so we avoid flickering and several tag 1386 -- selected at the same time. 1387 self.selected = true 1388 capi.screen[self.screen]:emit_signal("tag::history::update") 1389end 1390 1391--- View only a tag. 1392-- @deprecated awful.tag.viewonly 1393-- @see tag.view_only 1394-- @param t The tag object. 1395function tag.viewonly(t) 1396 gdebug.deprecate("Use t:view_only() instead of awful.tag.viewonly", {deprecated_in=4}) 1397 1398 tag.object.view_only(t) 1399end 1400 1401--- View only a set of tags. 1402-- 1403-- If `maximum` is set, there will be a limit on the number of new tag being 1404-- selected. The tags already selected do not count. To do nothing if one or 1405-- more of the tags are already selected, set `maximum` to zero. 1406-- 1407-- @function awful.tag.viewmore 1408-- @param tags A table with tags to view only. 1409-- @param[opt] screen The screen of the tags. 1410-- @tparam[opt=#tags] number maximum The maximum number of tags to select. 1411function tag.viewmore(tags, screen, maximum) 1412 maximum = maximum or #tags 1413 local selected = 0 1414 screen = get_screen(screen or ascreen.focused()) 1415 local screen_tags = screen.tags 1416 for _, _tag in ipairs(screen_tags) do 1417 if not gtable.hasitem(tags, _tag) then 1418 _tag.selected = false 1419 elseif _tag.selected then 1420 selected = selected + 1 1421 end 1422 end 1423 for _, _tag in ipairs(tags) do 1424 if selected == 0 and maximum == 0 then 1425 _tag.selected = true 1426 break 1427 end 1428 1429 if selected >= maximum then break end 1430 1431 if not _tag.selected then 1432 selected = selected + 1 1433 _tag.selected = true 1434 end 1435 end 1436 screen:emit_signal("tag::history::update") 1437end 1438 1439--- Toggle selection of a tag 1440-- @function awful.tag.viewtoggle 1441-- @see selected 1442-- @tparam tag t Tag to be toggled 1443function tag.viewtoggle(t) 1444 t.selected = not t.selected 1445 capi.screen[tag.getproperty(t, "screen")]:emit_signal("tag::history::update") 1446end 1447 1448--- Get tag data table. 1449-- 1450-- Do not use. 1451-- 1452-- @deprecated awful.tag.getdata 1453-- @tparam tag _tag The tag. 1454-- @return The data table. 1455function tag.getdata(_tag) 1456 return _tag.data.awful_tag_properties 1457end 1458 1459--- Get a tag property. 1460-- 1461-- Use `_tag.prop` directly. 1462-- 1463-- @deprecated awful.tag.getproperty 1464-- @tparam tag _tag The tag. 1465-- @tparam string prop The property name. 1466-- @return The property. 1467function tag.getproperty(_tag, prop) 1468 if not _tag then return end -- FIXME: Turn this into an error? 1469 if _tag.data.awful_tag_properties then 1470 return _tag.data.awful_tag_properties[prop] 1471 end 1472end 1473 1474--- Set a tag property. 1475-- This properties are internal to awful. Some are used to draw taglist, or to 1476-- handle layout, etc. 1477-- 1478-- Use `_tag.prop = value` 1479-- 1480-- @deprecated awful.tag.setproperty 1481-- @param _tag The tag. 1482-- @param prop The property name. 1483-- @param value The value. 1484function tag.setproperty(_tag, prop, value) 1485 if not _tag.data.awful_tag_properties then 1486 _tag.data.awful_tag_properties = {} 1487 end 1488 1489 if _tag.data.awful_tag_properties[prop] ~= value then 1490 _tag.data.awful_tag_properties[prop] = value 1491 _tag:emit_signal("property::" .. prop) 1492 end 1493end 1494 1495--- Tag a client with the set of current tags. 1496-- @deprecated awful.tag.withcurrent 1497-- @param c The client to tag. 1498function tag.withcurrent(c) 1499 gdebug.deprecate("Use c:to_selected_tags() instead of awful.tag.selectedlist", {deprecated_in=4}) 1500 1501 -- It can't use c:to_selected_tags() because awful.tag is loaded before 1502 -- awful.client 1503 1504 local tags = {} 1505 for _, t in ipairs(c:tags()) do 1506 if get_screen(tag.getproperty(t, "screen")) == get_screen(c.screen) then 1507 table.insert(tags, t) 1508 end 1509 end 1510 if #tags == 0 then 1511 tags = c.screen.selected_tags 1512 end 1513 if #tags == 0 then 1514 tags = c.screen.tags 1515 end 1516 if #tags ~= 0 then 1517 c:tags(tags) 1518 end 1519end 1520 1521local function attached_connect_signal_screen(screen, sig, func) 1522 screen = get_screen(screen) 1523 capi.tag.connect_signal(sig, function(_tag) 1524 if get_screen(tag.getproperty(_tag, "screen")) == screen then 1525 func(_tag) 1526 end 1527 end) 1528end 1529 1530--- Add a signal to all attached tags and all tags that will be attached in the 1531-- future. When a tag is detached from the screen, its signal is removed. 1532-- 1533-- @function awful.tag.attached_connect_signal 1534-- @screen screen The screen concerned, or all if nil. 1535-- @tparam[opt] string signal The signal name. 1536-- @tparam[opt] function Callback 1537function tag.attached_connect_signal(screen, ...) 1538 if screen then 1539 attached_connect_signal_screen(screen, ...) 1540 else 1541 capi.tag.connect_signal(...) 1542 end 1543end 1544 1545-- Register standard signals. 1546capi.client.connect_signal("property::screen", function(c) 1547 -- First, the delayed timer is necessary to avoid a race condition with 1548 -- awful.rules. It is also messing up the tags before the user have a chance 1549 -- to set them manually. 1550 timer.delayed_call(function() 1551 if not c.valid then 1552 return 1553 end 1554 local tags, new_tags = c:tags(), {} 1555 1556 for _, t in ipairs(tags) do 1557 if t.screen == c.screen then 1558 table.insert(new_tags, t) 1559 end 1560 end 1561 1562 if #new_tags == 0 then 1563 c:emit_signal("request::tag", nil, {reason="screen"}) 1564 elseif #new_tags < #tags then 1565 c:tags(new_tags) 1566 end 1567 end) 1568end) 1569 1570-- Keep track of the number of urgent clients. 1571local function update_urgent(t, modif) 1572 local count = tag.getproperty(t, "urgent_count") or 0 1573 count = (count + modif) >= 0 and (count + modif) or 0 1574 tag.setproperty(t, "urgent" , count > 0) 1575 tag.setproperty(t, "urgent_count", count ) 1576end 1577 1578-- Update the urgent counter when a client is tagged. 1579local function client_tagged(c, t) 1580 if c.urgent then 1581 update_urgent(t, 1) 1582 end 1583end 1584 1585-- Update the urgent counter when a client is untagged. 1586local function client_untagged(c, t) 1587 if c.urgent then 1588 update_urgent(t, -1) 1589 end 1590 1591 if #t:clients() == 0 and tag.getproperty(t, "volatile") then 1592 tag.object.delete(t) 1593 end 1594end 1595 1596-- Count the urgent clients. 1597local function urgent_callback(c) 1598 for _,t in ipairs(c:tags()) do 1599 update_urgent(t, c.urgent and 1 or -1) 1600 end 1601end 1602 1603capi.client.connect_signal("property::urgent", urgent_callback) 1604capi.client.connect_signal("untagged", client_untagged) 1605capi.client.connect_signal("tagged", client_tagged) 1606capi.tag.connect_signal("request::select", tag.object.view_only) 1607 1608--- True when a tagged client is urgent 1609-- @signal property::urgent 1610-- @see client.urgent 1611 1612--- The number of urgent tagged clients 1613-- @signal property::urgent_count 1614-- @see client.urgent 1615 1616--- Emitted when a screen is removed. 1617-- This can be used to salvage existing tags by moving them to a new 1618-- screen (or creating a virtual screen). By default, there is no 1619-- handler for this request. The tags will be deleted. To prevent 1620-- this, an handler for this request must simply set a new screen 1621-- for the tag. 1622-- @signal request::screen 1623 1624--- Emitted after `request::screen` if no new screen has been set. 1625-- The tag will be deleted, this is a last chance to move its clients 1626-- before they are sent to a fallback tag. Connect to `request::screen` 1627-- if you wish to salvage the tag. 1628-- @signal removal-pending 1629 1630capi.screen.connect_signal("tag::history::update", tag.history.update) 1631 1632-- Make sure the history is set early 1633timer.delayed_call(function() 1634 for s in capi.screen do 1635 s:emit_signal("tag::history::update") 1636 end 1637end) 1638 1639capi.screen.connect_signal("removed", function(s) 1640 -- First give other code a chance to move the tag to another screen 1641 for _, t in pairs(s.tags) do 1642 t:emit_signal("request::screen") 1643 end 1644 -- Everything that's left: Tell everyone that these tags go away (other code 1645 -- could e.g. save clients) 1646 for _, t in pairs(s.tags) do 1647 t:emit_signal("removal-pending") 1648 end 1649 -- Give other code yet another change to save clients 1650 for _, c in pairs(capi.client.get(s)) do 1651 c:emit_signal("request::tag", nil, { reason = "screen-removed" }) 1652 end 1653 -- Then force all clients left to go somewhere random 1654 local fallback = nil 1655 for other_screen in capi.screen do 1656 if #other_screen.tags > 0 then 1657 fallback = other_screen.tags[1] 1658 break 1659 end 1660 end 1661 for _, t in pairs(s.tags) do 1662 t:delete(fallback, true) 1663 end 1664 -- If any tag survived until now, forcefully get rid of it 1665 for _, t in pairs(s.tags) do 1666 t.activated = false 1667 1668 if t.data.awful_tag_properties then 1669 t.data.awful_tag_properties.screen = nil 1670 end 1671 end 1672end) 1673 1674function tag.mt:__call(...) 1675 return tag.new(...) 1676end 1677 1678-- Extend the luaobject 1679-- `awful.tag.setproperty` currently handle calling the setter method itself 1680-- while `awful.tag.getproperty`. 1681object.properties(capi.tag, { 1682 getter_class = tag.object, 1683 setter_class = tag.object, 1684 getter_fallback = tag.getproperty, 1685 setter_fallback = tag.setproperty, 1686}) 1687 1688return setmetatable(tag, tag.mt) 1689 1690-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 1691