value_text_box.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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 displayed text value format string
  68. --@usage mypgraph:set_value_format(string) --> "%2.f"
  69. --@name set_value_format
  70. --@class function
  71. --@param printf format string for display text
  72. ---Define the template of the text to display.
  73. --@usage myvt_box:set_label(string)
  74. --By default the text is : (value_send_to_the_widget *100)
  75. --static string: example set_label("CPU usage:") will display "CUP usage:" on the vt_box
  76. --dynamic string: use $percent in the string example set_label("Load $percent %") will display "Load 10%"
  77. --@name set_label
  78. --@class function
  79. --@param text the text to display
  80. local properties = { "width", "height", "h_margin", "v_margin", "padding",
  81. "background_color",
  82. "background_text_border", "text_background_color",
  83. "rounded_size", "text_color", "values_text_color",
  84. "font_size", "font", "label", "value_format"
  85. }
  86. function value_text_box.draw(vt_box, wibox, cr, width, height)
  87. --find the width of our image
  88. local props = helpers.load_properties(properties, data, vt_box, superproperties)
  89. local value = string.format(props.value_format,
  90. data[vt_box].value * 100)
  91. -- local label = data[vt_box].label or "$percent"
  92. local text = string.gsub(props.label,"$percent", value)
  93. cr:set_font_size(props.font_size)
  94. if type(props.font) == "string" then
  95. cr:select_font_face(props.font,nil,nil)
  96. elseif type(props.font) == "table" then
  97. cr:select_font_face(props.font.family or "Sans",
  98. props.font.slang or "normal",
  99. props.font.weight or "normal")
  100. end
  101. local ext = cr:text_extents(text)
  102. local height = ( (props.font_size + 2* props.padding + 2* props.v_margin) > data[vt_box].height ) and props.font_size + 2* props.padding + 2* props.v_margin or data[vt_box].height
  103. local width = ( math.ceil(ext.width +2*ext.x_bearing+ 2*props.padding + 2* props.h_margin) > data[vt_box].width ) and math.ceil(ext.width +2*ext.x_bearing+ 2*props.padding + 2* props.h_margin) or data[vt_box].width
  104. if data[vt_box].width == nil or data[vt_box].width < width then
  105. data[vt_box].width = width
  106. end
  107. if data[vt_box].height == nil or data[vt_box].height < height then
  108. data[vt_box].height = height
  109. end
  110. --Generate Background (background widget)
  111. if data[vt_box].background_color then
  112. helpers.draw_rounded_corners_rectangle( cr,
  113. 0,
  114. 0,
  115. data[vt_box].width,
  116. data[vt_box].height,
  117. props.background_color,
  118. props.rounded_size)
  119. end
  120. --Draw nothing, or filled ( value background)
  121. if data[vt_box].text_background_color then
  122. --draw rounded corner rectangle
  123. local x=props.h_margin
  124. local y=props.v_margin
  125. helpers.draw_rounded_corners_rectangle( cr,
  126. x,
  127. y,
  128. data[vt_box].width - props.h_margin,
  129. data[vt_box].height - props.v_margin,
  130. props.text_background_color,
  131. props.rounded_size,
  132. props.background_text_border
  133. )
  134. end
  135. --draw the value
  136. --analyse the label :
  137. label_parts={}
  138. label_length = string.len(props.label)
  139. value_start, value_finish = string.find(props.label, "$percent")
  140. table.insert(label_parts,{1, value_start -1})
  141. table.insert(label_parts,{value_start, value_finish,is_value = true})
  142. local new_start = value_finish + 1
  143. if value_finish < label_length then
  144. while value_start ~= nil do
  145. value_start, value_finish = string.find(props.label, "$percent", new_start)
  146. if value_start ~=nil and value_start ~= new_start then
  147. table.insert(label_parts,{new_start, value_start -1})
  148. table.insert(label_parts,{value_start, value_finish, is_value = true})
  149. new_start = value_finish + 1
  150. elseif value_start == new_start then
  151. table.insert(label_parts,{value_start, value_finish, is_value = true})
  152. new_start = value_finish + 1
  153. end
  154. end
  155. --if rest char:
  156. if label_parts[#label_parts][2] < label_length then
  157. table.insert(label_parts,{label_parts[#label_parts][2] + 1, label_length})
  158. end
  159. end
  160. --Position the x start of the text. If rounded_corner, then we move the text on the right
  161. local move_because_of_rounded_corners=0
  162. if width> height then
  163. move_because_of_rounded_corners=((height/2) * props.rounded_size)/2
  164. else
  165. move_because_of_rounded_corners=((width/2) * props.rounded_size)/2
  166. end
  167. x= props.h_margin + props.padding + move_because_of_rounded_corners
  168. y= 0
  169. --Draw the text:
  170. -- TODO necessity for this variable (default_text_color ??)
  171. local default_text_color =props.text_color
  172. local value_text_color = default_text_color
  173. for i,table in ipairs(props.values_text_color) do
  174. if i == 1 then
  175. if value/100 >= table[2] then
  176. value_text_color = table[1]
  177. end
  178. elseif i ~= 1 then
  179. if value/100 >= table[2] then
  180. value_text_color = table[1]
  181. end
  182. end
  183. end
  184. for i,range in ipairs(label_parts) do
  185. if range.is_value then
  186. text = value
  187. --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:
  188. for j=range[1],label_parts[i-1][1], -1 do
  189. if string.sub(props.label, j,j) == " " then
  190. text = " " .. text
  191. end
  192. end
  193. r,g,b,a = helpers.hexadecimal_to_rgba_percent(value_text_color)
  194. cr:set_source_rgba(r,g,b,a)
  195. ext = cr:text_extents(text)
  196. y=math.floor(data[vt_box].height/2 + props.font_size/2 - props.padding/2)
  197. cr:set_font_size(props.font_size)
  198. cr:move_to(x,y)
  199. cr:show_text(text)
  200. x=x+ext.width + ext.x_bearing
  201. else
  202. text=string.sub(props.label, range[1],range[2])
  203. text=string.gsub(text, "(.*[^%s]*)(%s*)","%1")
  204. r,g,b,a = helpers.hexadecimal_to_rgba_percent(props.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 + props.font_size/2 - props.padding/2)
  208. cr:set_font_size(props.font_size)
  209. cr:move_to(x,y)
  210. cr:show_text(text)
  211. x=x+ext.width --+ext.x_bearing
  212. end
  213. end
  214. end
  215. function value_text_box.fit(vt_box, width, height)
  216. return data[vt_box].width, data[vt_box].height
  217. end
  218. --- Add a value to the vt_box.
  219. -- For compatibility between old and new awesome widget, add_value can be replaced by set_value
  220. -- @usage myvt_box:add_value(a) or myvt_box:set_value(a)
  221. -- @param vt_box The vt_box.
  222. -- @param value The value between 0 and 1.
  223. local function add_value(vt_box, value)
  224. if not vt_box then return end
  225. local value = value or 0
  226. if string.find(value, "nan") then
  227. value=0
  228. end
  229. data[vt_box].value = value
  230. vt_box:emit_signal("widget::updated")
  231. return vt_box
  232. end
  233. --- Set the vt_box height.
  234. -- @param height The height to set.
  235. function value_text_box:set_height( height)
  236. if height >= 5 then
  237. data[self].height = height
  238. self:emit_signal("widget::updated")
  239. end
  240. return self
  241. end
  242. --- Set the vt_box width.
  243. -- @param width The width to set.
  244. function value_text_box:set_width( width)
  245. if width >= 5 then
  246. data[self].width = width
  247. self:emit_signal("widget::updated")
  248. end
  249. return self
  250. end
  251. -- Build properties function
  252. for _, prop in ipairs(properties) do
  253. if not value_text_box["set_" .. prop] then
  254. value_text_box["set_" .. prop] = function(vt_box, value)
  255. data[vt_box][prop] = value
  256. vt_box:emit_signal("widget::updated")
  257. return vt_box
  258. end
  259. end
  260. end
  261. --- Create a vt_box widget.
  262. -- @param args Standard widget() arguments. You can add width and height keys to set vt_box geometry.
  263. -- @return A vt_box widget.
  264. function value_text_box.new(args)
  265. local args = args or {}
  266. args.width = args.width or 100
  267. args.height = args.height or 20
  268. if args.width < 5 or args.height < 5 then return end
  269. local vt_box = base.make_widget()
  270. data[vt_box] = {}
  271. for _, v in ipairs(properties) do
  272. data[vt_box][v] = args[v]
  273. end
  274. -- Set methods
  275. vt_box.set_value = set_value
  276. vt_box.add_value = add_value
  277. vt_box.draw = value_text_box.draw
  278. vt_box.fit = value_text_box.fit
  279. for _, prop in ipairs(properties) do
  280. vt_box["set_" .. prop] = value_text_box["set_" .. prop]
  281. end
  282. return vt_box
  283. end
  284. function value_text_box.mt:__call(...)
  285. return value_text_box.new(...)
  286. end
  287. return setmetatable(value_text_box, value_text_box.mt)