value_text_box.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. -- @author cedlemo
  2. local setmetatable = setmetatable
  3. local ipairs = ipairs
  4. local math = math
  5. local table = table
  6. local type = type
  7. local string = string
  8. local color = require("gears.color")
  9. local base = require("wibox.widget.base")
  10. local helpers = require("blingbling.helpers")
  11. local superproperties = require("blingbling.superproperties")
  12. ---A text box that can display value and text with colors.
  13. --@module blingbling.value_text_box
  14. local value_text_box = { mt = {} }
  15. local data = setmetatable({}, { __mode = "k" })
  16. ---Fill all the widget with this color (default is transparent).
  17. --@usage myvt_box:set_background_color(string) -->"#rrggbbaa"
  18. --@name set_background_color
  19. --@class function
  20. --@param color a string "#rrggbbaa" or "#rrggbb"
  21. ---Fill the text area (text height/width + padding) background with this color (default is none).
  22. --@usage myvt_box:set_text_background_color(string) -->"#rrggbbaa"
  23. --@name set_text_background_color
  24. --@class function
  25. --@param color a string "#rrggbbaa" or "#rrggbb"
  26. ---Set a border on the text area background (default is none ).
  27. --@usage myvt_box:set_text_background_border(string) -->"#rrggbbaa"
  28. --@name set_text_background_border
  29. --@class function
  30. --@param color a string "#rrggbbaa" or "#rrggbb"
  31. ---Define the top and bottom margin for the text background.
  32. --@usage myvt_box:set_v_margin(integer)
  33. --@name set_v_margin
  34. --@class function
  35. --@param margin an integer for top and bottom margin
  36. ---Define the left and right margin for the text background.
  37. --@usage myvt_box:set_h_margin(integer)
  38. --@name set_h_margin
  39. --@class function
  40. --@param margin an integer for left and right margin
  41. ---Define the padding between the text and it's background.
  42. --@usage myvt_box:set_padding(integer)
  43. --@name set_h_margin
  44. --@class function
  45. --@param padding an integer for the text padding
  46. ---Set rounded corners for background and text background.
  47. --@usage myvt_box:set_rounded_size(a) -> a in [0,1]
  48. --@name set_rounded_size
  49. --@class function
  50. --@param rounded_size float in [0,1]
  51. ---Define the color for all the text of the widget (white by default).
  52. --@usage myvt_box:set_text_color(string) -->"#rrggbbaa"
  53. --@name set_text_color
  54. --@class function
  55. --@param color a string "#rrggbbaa" or "#rrggbb
  56. ---Define the value text color depending on the limit given ( if value >= limit, we apply the color).
  57. --@usage myvt_box:set_values_text_color(table) -->{ {"#rrggbbaa", limit 1}, {"#rrggbbaa", limit 2}}
  58. -- By default the color is text_color(another example: {{"#88aa00ff",0},{"#d4aa00ff", 0.5},{"#d45500ff",0.75}})
  59. --@name set_values_text_color
  60. --@class function
  61. --@param table table like { {"#rrggbbaa", limit 1}, {"#rrggbbaa", limit 2}}
  62. ---Define the text font size.
  63. --@usage myvt_box:set_font_size(integer)
  64. --@name set_font_size
  65. --@class function
  66. --@param size the font size
  67. ---Define the template of the text to display.
  68. --@usage myvt_box:set_label(string)
  69. --By default the text is : (value_send_to_the_widget *100)
  70. --static string: example set_label("CPU usage:") will display "CUP usage:" on the vt_box
  71. --dynamic string: use $percent in the string example set_label("Load $percent %") will display "Load 10%"
  72. --@name set_label
  73. --@class function
  74. --@param text the text to display
  75. local properties = { "width", "height", "h_margin", "v_margin", "padding",
  76. "background_color",
  77. "background_text_border", "text_background_color",
  78. "rounded_size", "text_color", "values_text_color",
  79. "font_size", "font", "label"
  80. }
  81. function value_text_box.draw(vt_box, wibox, cr, width, height)
  82. local v_margin = superproperties.v_margin
  83. if data[vt_box].v_margin and data[vt_box].v_margin <= data[vt_box].height/3 then
  84. v_margin = data[vt_box].v_margin
  85. end
  86. local h_margin = superproperties.h_margin
  87. if data[vt_box].h_margin and data[vt_box].h_margin <= data[vt_box].width / 3 then
  88. h_margin = data[vt_box].h_margin
  89. end
  90. local padding = data[vt_box].padding or superproperties.padding
  91. local background_color = data[vt_box].background_color or superproperties.background_color
  92. local rounded_size = data[vt_box].rounded_size or superproperties.rounded_size
  93. local graph_background_color = data[vt_box].graph_background_color or superproperties.graph_background_color
  94. local graph_background_border = data[vt_box].graph_background_border or superproperties.graph_background_border
  95. local graph_color = data[vt_box].graph_color or superproperties.graph_color
  96. local graph_line_color = data[vt_box].graph_line_color or superproperties.graph_line_color
  97. local text_color = data[vt_box].text_color or superproperties.text_color
  98. local text_background_color = data[vt_box].text_background_color or superproperties.text_background_color
  99. local font_size =data[vt_box].font_size or superproperties.font_size
  100. local font = data[vt_box].font or superproperties.font
  101. --find the width of our image
  102. local value = (data[vt_box].value or 0) * 100
  103. local label = data[vt_box].label or "$percent"
  104. local text = string.gsub(label,"$percent", value)
  105. cr:set_font_size(font_size)
  106. if type(font) == "string" then
  107. cr:select_font_face(font,nil,nil)
  108. elseif type(font) == "table" then
  109. cr:select_font_face(font.family or "Sans", font.slang or "normal", font.weight or "normal")
  110. end
  111. local ext = cr:text_extents(text)
  112. local height = ( (font_size + 2* padding + 2* v_margin) > data[vt_box].height ) and font_size + 2* padding + 2* v_margin or data[vt_box].height
  113. local width = ( math.ceil(ext.width +2*ext.x_bearing+ 2*padding + 2* h_margin) > data[vt_box].width ) and math.ceil(ext.width +2*ext.x_bearing+ 2*padding + 2* h_margin) or data[vt_box].width
  114. if data[vt_box].width == nil or data[vt_box].width < width then
  115. data[vt_box].width = width
  116. end
  117. if data[vt_box].height == nil or data[vt_box].height < height then
  118. data[vt_box].height = height
  119. end
  120. --Generate Background (background widget)
  121. if data[vt_box].background_color then
  122. helpers.draw_rounded_corners_rectangle( cr,
  123. 0,
  124. 0,
  125. data[vt_box].width,
  126. data[vt_box].height,
  127. background_color,
  128. rounded_size)
  129. end
  130. --Draw nothing, or filled ( value background)
  131. if data[vt_box].text_background_color then
  132. --draw rounded corner rectangle
  133. local x=h_margin
  134. local y=v_margin
  135. helpers.draw_rounded_corners_rectangle( cr,
  136. x,
  137. y,
  138. data[vt_box].width - h_margin,
  139. data[vt_box].height - v_margin,
  140. text_background_color,
  141. rounded_size,
  142. background_text_border
  143. )
  144. end
  145. --draw the value
  146. --analyse the label :
  147. label_parts={}
  148. label_length = string.len(label)
  149. value_start, value_finish = string.find(label, "$percent")
  150. table.insert(label_parts,{1, value_start -1})
  151. table.insert(label_parts,{value_start, value_finish,is_value = true})
  152. local new_start = value_finish + 1
  153. if value_finish < label_length then
  154. while value_start ~= nil do
  155. value_start, value_finish = string.find(label, "$percent", new_start)
  156. if value_start ~=nil and value_start ~= new_start then
  157. table.insert(label_parts,{new_start, value_start -1})
  158. table.insert(label_parts,{value_start, value_finish, is_value = true})
  159. new_start = value_finish + 1
  160. elseif value_start == new_start then
  161. table.insert(label_parts,{value_start, value_finish, is_value = true})
  162. new_start = value_finish + 1
  163. end
  164. end
  165. --if rest char:
  166. if label_parts[#label_parts][2] < label_length then
  167. table.insert(label_parts,{label_parts[#label_parts][2] + 1, label_length})
  168. end
  169. end
  170. --Position the x start of the text. If rounded_corner, then we move the text on the right
  171. local move_because_of_rounded_corners=0
  172. if width> height then
  173. move_because_of_rounded_corners=((height/2) * rounded_size)/2
  174. else
  175. move_because_of_rounded_corners=((width/2) * rounded_size)/2
  176. end
  177. x= h_margin + padding + move_because_of_rounded_corners
  178. y= 0
  179. --Draw the text:
  180. local default_text_color = text_color
  181. local value_text_color = default_text_color
  182. if data[vt_box].values_text_color and type(data[vt_box].values_text_color) == "table" then
  183. for i,table in ipairs(data[vt_box].values_text_color) do
  184. if i == 1 then
  185. if value/100 >= table[2] then
  186. value_text_color = table[1]
  187. end
  188. elseif i ~= 1 then
  189. if value/100 >= table[2] then
  190. value_text_color = table[1]
  191. end
  192. end
  193. end
  194. end
  195. for i,range in ipairs(label_parts) do
  196. if range.is_value then
  197. text = value
  198. --dirty trick : if there are spaces at the end of cairo text, they aren't represented so I put them at the begining of the value:
  199. for j=range[1],label_parts[i-1][1], -1 do
  200. if string.sub(label, j,j) == " " then
  201. text = " " .. text
  202. end
  203. end
  204. r,g,b,a = helpers.hexadecimal_to_rgba_percent(value_text_color)
  205. cr:set_source_rgba(r,g,b,a)
  206. ext = cr:text_extents(text)
  207. y=math.floor(data[vt_box].height/2 + font_size/2 - padding/2)
  208. cr:set_font_size(font_size)
  209. cr:move_to(x,y)
  210. cr:show_text(text)
  211. x=x+ext.width + ext.x_bearing
  212. else
  213. text=string.sub(label, range[1],range[2])
  214. text=string.gsub(text, "(.*[^%s]*)(%s*)","%1")
  215. r,g,b,a = helpers.hexadecimal_to_rgba_percent(default_text_color)
  216. cr:set_source_rgba(r,g,b,a)
  217. ext = cr:text_extents(text)
  218. y=math.floor(data[vt_box].height/2 + font_size/2 - padding/2)
  219. cr:set_font_size(font_size)
  220. cr:move_to(x,y)
  221. cr:show_text(text)
  222. x=x+ext.width --+ext.x_bearing
  223. end
  224. end
  225. end
  226. function value_text_box.fit(vt_box, width, height)
  227. return data[vt_box].width, data[vt_box].height
  228. end
  229. --- Add a value to the vt_box.
  230. -- For compatibility between old and new awesome widget, add_value can be replaced by set_value
  231. -- @usage myvt_box:add_value(a) or myvt_box:set_value(a)
  232. -- @param vt_box The vt_box.
  233. -- @param value The value between 0 and 1.
  234. local function add_value(vt_box, value)
  235. if not vt_box then return end
  236. local value = value or 0
  237. if string.find(value, "nan") then
  238. value=0
  239. end
  240. data[vt_box].value = value
  241. vt_box:emit_signal("widget::updated")
  242. return vt_box
  243. end
  244. --- Set the vt_box height.
  245. -- @param height The height to set.
  246. function value_text_box:set_height( height)
  247. if height >= 5 then
  248. data[self].height = height
  249. self:emit_signal("widget::updated")
  250. end
  251. return self
  252. end
  253. --- Set the vt_box width.
  254. -- @param width The width to set.
  255. function value_text_box:set_width( width)
  256. if width >= 5 then
  257. data[self].width = width
  258. self:emit_signal("widget::updated")
  259. end
  260. return self
  261. end
  262. -- Build properties function
  263. for _, prop in ipairs(properties) do
  264. if not value_text_box["set_" .. prop] then
  265. value_text_box["set_" .. prop] = function(vt_box, value)
  266. data[vt_box][prop] = value
  267. vt_box:emit_signal("widget::updated")
  268. return vt_box
  269. end
  270. end
  271. end
  272. --- Create a vt_box widget.
  273. -- @param args Standard widget() arguments. You can add width and height keys to set vt_box geometry.
  274. -- @return A vt_box widget.
  275. function value_text_box.new(args)
  276. local args = args or {}
  277. args.width = args.width or 100
  278. args.height = args.height or 20
  279. if args.width < 5 or args.height < 5 then return end
  280. local vt_box = base.make_widget()
  281. data[vt_box] = {}
  282. for _, v in ipairs(properties) do
  283. data[vt_box][v] = args[v]
  284. end
  285. -- Set methods
  286. vt_box.set_value = set_value
  287. vt_box.add_value = add_value
  288. vt_box.draw = value_text_box.draw
  289. vt_box.fit = value_text_box.fit
  290. for _, prop in ipairs(properties) do
  291. vt_box["set_" .. prop] = value_text_box["set_" .. prop]
  292. end
  293. return vt_box
  294. end
  295. function value_text_box.mt:__call(...)
  296. return value_text_box.new(...)
  297. end
  298. return setmetatable(value_text_box, value_text_box.mt)