tagslist.lua 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. --@author cedlemo
  2. local capi = { screen = screen,
  3. awesome = awesome,
  4. client = client }
  5. local type = type
  6. local setmetatable = setmetatable
  7. local pairs = pairs
  8. local ipairs = ipairs
  9. local table = table
  10. local common = require("awful.widget.common")
  11. local util = require("awful.util")
  12. local tag = require("awful.tag")
  13. local beautiful = require("beautiful")
  14. local wibox = require("wibox")
  15. local fixed = require("wibox.layout.fixed")
  16. local surface = require("gears.surface")
  17. local helpers = require("blingbling.helpers")
  18. local text_box = require("blingbling.text_box")
  19. local superproperties = require("blingbling.superproperties")
  20. ---Tagslist widget. Based on the original taglist widget from awesome
  21. --@module blingbling.tagslist
  22. local taglist = { mt = {} }
  23. taglist.filter = {}
  24. local function taglist_customize(t, style)
  25. if (not style or (type(style) ~= "table")) then
  26. style = {}
  27. end
  28. local style_normal = {}
  29. local style_focus = {}
  30. local style_urgent = {}
  31. local style_occupied = {}
  32. if style.normal and type(style.normal) == "table" then
  33. for k, v in pairs(superproperties.tagslist.normal) do
  34. style_normal[v] = style.normal[v] or superproperties.tagslist.normal[v]
  35. end
  36. else
  37. style_normal = superproperties.tagslist.normal
  38. end
  39. if style.focus and type(style.focus) == "table" then
  40. for k, v in pairs(superproperties.tagslist.focus) do
  41. style_focus[v] = style.focus[v] or superproperties.tagslist.focus[v]
  42. end
  43. else
  44. style_focus = superproperties.tagslist.focus
  45. end
  46. if style.urgent and type(style.urgent) == "table" then
  47. for k, v in pairs(superproperties.tagslist.urgent) do
  48. style_urgent[v] = style.urgent[v] or superproperties.tagslist.urgent[v]
  49. end
  50. else
  51. style_urgent = superproperties.tagslist.urgent
  52. end
  53. if style.occupied and type(style.occupied) == "table" then
  54. for k, v in pairs(superproperties.tagslist.occupied) do
  55. style_occupied[v] = style.occupied[v] or superproperties.tagslist.occupied[v]
  56. end
  57. else
  58. style_occupied = superproperties.tagslist.occupied
  59. end
  60. local current_style = style_normal
  61. local theme = beautiful.get()
  62. local taglist_squares_sel = style.squares_sel or theme.taglist_squares_sel
  63. local taglist_squares_unsel = style.squares_unsel or theme.taglist_squares_unsel
  64. local taglist_squares_sel_empty = style.squares_sel_empty or theme.taglist_squares_sel_empty
  65. local taglist_squares_unsel_empty = style.squares_unsel_empty or theme.taglist_squares_unsel_empty
  66. local taglist_squares_resize = style.squares_resize or theme.taglist_squares_resize or true
  67. local taglist_disable_icon = style.taglist_disable_icon or theme.taglist_disable_icon or true
  68. local text = ""
  69. local sel = capi.client.focus
  70. local bg_image
  71. local icon
  72. local bg_resize = false
  73. local is_selected = false
  74. if t.selected then
  75. current_style = style_focus
  76. end
  77. if sel then
  78. if taglist_squares_sel then
  79. local seltags = sel:tags()
  80. for _, v in ipairs(seltags) do
  81. if v == t then
  82. bg_image = taglist_squares_sel
  83. bg_resize = taglist_squares_resize == "true"
  84. is_selected = true
  85. break
  86. end
  87. end
  88. end
  89. end
  90. if t:clients() == 0 and t.selected and taglist_squares_sel_empty then
  91. bg_image = taglist_squares_sel_empty
  92. bg_resize = taglist_squares_resize == "true"
  93. elseif not is_selected then
  94. local cls = t:clients()
  95. if #cls > 0 then
  96. if taglist_squares_unsel then
  97. bg_image = taglist_squares_unsel
  98. bg_resize = taglist_squares_resize == "true"
  99. end
  100. current_style = style_occupied
  101. else
  102. if taglist_squares_unsel_empty then
  103. bg_image = taglist_squares_unsel_empty
  104. bg_resize = taglist_squares_resize == "true"
  105. end
  106. end
  107. for k, c in pairs(cls) do
  108. if c.urgent then
  109. current_style = style_urgent
  110. break
  111. end
  112. end
  113. end
  114. if not tag.getproperty(t, "icon_only") then
  115. text = (util.escape(t.name) or "")
  116. end
  117. if not taglist_disable_icon then
  118. if tag.geticon(t) and type(tag.geticon(t)) == "image" then
  119. icon = tag.geticon(t)
  120. elseif tag.geticon(t) then
  121. icon = surface.load(tag.geticon(t))
  122. end
  123. end
  124. return text , current_style, bg_image, not taglist_disable_icon and icon or nil
  125. end
  126. local function taglist_update_style(w, buttons, style, data, tags)
  127. -- update the widgets, creating them if needed
  128. w:reset()
  129. for i, o in ipairs(tags) do
  130. local text, style, icon = taglist_customize(o, style)
  131. local cache = data[o]
  132. local ib, tb, bgb, m, l
  133. if cache then
  134. ib = cache.ib
  135. tb = cache.tb
  136. bgb = cache.bgb
  137. m = cache.m
  138. else
  139. ib = wibox.widget.imagebox()
  140. tb = text_box(style)
  141. bgb = wibox.widget.background()
  142. m = wibox.layout.margin(tb, 2, 2)
  143. l = wibox.layout.fixed.horizontal()
  144. -- All of this is added in a fixed widget
  145. l:fill_space(true)
  146. l:add(ib)
  147. l:add(m)
  148. -- And all of this gets a background
  149. bgb:set_widget(l)
  150. bgb:buttons(common.create_buttons(buttons, o))
  151. data[o] = {
  152. ib = ib,
  153. tb = tb,
  154. bgb = bgb,
  155. m = m
  156. }
  157. end
  158. -- The text might be invalid, so use pcall
  159. if not pcall(tb.set_text, tb, text) then
  160. tb:set_text("<i>&lt;Invalid text&gt;</i>")
  161. end
  162. tb:set_h_margin(style.h_margin)
  163. tb:set_v_margin(style.v_margin)
  164. tb:set_rounded_size(style.rounded_size)
  165. tb:set_text_color(style.text_color)
  166. tb:set_font(style.font)
  167. tb:set_font_size(style.font_size)
  168. tb:set_background_color(style.background_color)
  169. tb:set_text_background_color(style.text_background_color)
  170. bgb:set_bg(bg)
  171. if type(bg_image) == "function" then
  172. bg_image = bg_image(tb,o,m,objects,i)
  173. end
  174. bgb:set_bgimage(bg_image)
  175. ib:set_image(icon)
  176. w:add(bgb)
  177. end
  178. end
  179. local function taglist_update(s, w, buttons, filter, data, style)
  180. local tags = {}
  181. for k, t in ipairs(tag.gettags(s)) do
  182. if not tag.getproperty(t, "hide") and filter(t) then
  183. table.insert(tags, t)
  184. end
  185. end
  186. taglist_update_style(w, buttons, style, data, tags)
  187. end
  188. --- Get the tag object the given widget appears on.
  189. -- @param widget The widget the look for.
  190. -- @return The tag object.
  191. function taglist.gettag(widget)
  192. return common.tagwidgets[widget]
  193. end
  194. ---Create a new tagslist widget
  195. --@usage mytags = blingbling.tagslist(screen, filter, buttons,{normal={blingbling.text_box parameters}, focus = {blingbling.text_box parameters},urgent = {blingbling.text_box parameters}, occupied = {blingbling.text_box parameters}}, an awful.layout)
  196. --@param screen an integer for the screen of the tagslist
  197. --@param filter a filtering function the default check if the current tag is not empty and return true if so.
  198. --@param buttons a table of awful.button (mytaglist.buttons from the default rc.lua of awesome for example)
  199. --@param style an optional table with contains 4 tables of text_box parameters in order to change the apparence of the tag based on its state (normal, focus, urgent, occupied)
  200. --@param base_widget an awful.layout (horizontal or vertical)
  201. function taglist.new(screen, filter, buttons, style, base_widget)
  202. local w = base_widget or fixed.horizontal()
  203. local data = setmetatable({}, { __mode = 'k' })
  204. local u = function (s)
  205. if s == screen then
  206. taglist_update(s, w, buttons, filter, data, style)
  207. end
  208. end
  209. local uc = function (c) return u(c.screen) end
  210. local ut = function (t) return u(tag.getscreen(t)) end
  211. capi.client.connect_signal("focus", uc)
  212. capi.client.connect_signal("unfocus", uc)
  213. tag.attached_connect_signal(screen, "property::selected", ut)
  214. tag.attached_connect_signal(screen, "property::icon", ut)
  215. tag.attached_connect_signal(screen, "property::hide", ut)
  216. tag.attached_connect_signal(screen, "property::name", ut)
  217. tag.attached_connect_signal(screen, "property::activated", ut)
  218. tag.attached_connect_signal(screen, "property::screen", ut)
  219. tag.attached_connect_signal(screen, "property::index", ut)
  220. capi.client.connect_signal("property::urgent", uc)
  221. capi.client.connect_signal("property::screen", function(c)
  222. -- If client change screen, refresh it anyway since we don't from
  223. -- which screen it was coming :-)
  224. u(screen)
  225. end)
  226. capi.client.connect_signal("tagged", uc)
  227. capi.client.connect_signal("untagged", uc)
  228. capi.client.connect_signal("unmanage", uc)
  229. u(screen)
  230. return w
  231. end
  232. --- Filtering function to include all nonempty tags on the screen.
  233. -- @param t The tag.
  234. -- @param args unused list of extra arguments.
  235. -- @return true if t is not empty, else false
  236. function taglist.filter.noempty(t, args)
  237. return #t:clients() > 0 or t.selected
  238. end
  239. --- Filtering function to include all tags on the screen.
  240. -- @param t The tag.
  241. -- @param args unused list of extra arguments.
  242. -- @return true
  243. function taglist.filter.all(t, args)
  244. return true
  245. end
  246. function taglist.mt:__call(...)
  247. return taglist.new(...)
  248. end
  249. return setmetatable(taglist, taglist.mt)