Simplify the graph drawing code
[awesome.git] / lib / awful / widget / graph.lua.in
blob7b2a3c52d2dcf7f59da3dad722fbf88f54fa847e
1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2009 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 local setmetatable = setmetatable
8 local ipairs = ipairs
9 local math = math
10 local table = table
11 local type = type
12 local color = require("gears.color")
13 local base = require("wibox.widget.base")
15 --- A graph widget.
16 module("awful.widget.graph")
18 local data = setmetatable({}, { __mode = "k" })
20 --- Set the graph border color.
21 -- If the value is nil, no border will be drawn.
22 -- @name set_border_color
23 -- @class function
24 -- @param graph The graph.
25 -- @param color The border color to set.
27 --- Set the graph foreground color.
28 -- @name set_color
29 -- @class function
30 -- @param graph The graph.
31 -- @param color The graph color.
33 --- Set the graph background color.
34 -- @name set_background_color
35 -- @class function
36 -- @param graph The graph.
37 -- @param color The graph background color.
39 --- Set the maximum value the graph should handle.
40 -- If "scale" is also set, the graph never scales up below this value, but it
41 -- automatically scales down to make all data fit.
42 -- @name set_max_value
43 -- @class function
44 -- @param graph The graph.
45 -- @param value The value.
47 --- Set the graph to automatically scale its values. Default is false.
48 -- @name set_scale
49 -- @class function
50 -- @param graph The graph.
51 -- @param scale A boolean value
53 --- Set the graph to draw stacks. Default is false.
54 -- @name set_stack
55 -- @class function
56 -- @param graph The graph.
57 -- @param stack A boolean value.
59 --- Set the graph stacking colors. Order matters.
60 -- @name set_stack_colors
61 -- @class function
62 -- @param graph The graph.
63 -- @param stack_colors A table with stacking colors.
65 local properties = { "width", "height", "border_color", "stack",
66 "stack_colors", "color", "background_color",
67 "max_value", "scale" }
69 function draw(graph, wibox, cr, width, height)
70 local max_value = data[graph].max_value
71 local values = data[graph].values
73 cr:set_line_width(1)
75 -- Draw the background first
76 cr:set_source(color(data[graph].background_color or "#000000aa"))
77 cr:paint()
79 -- Account for the border width
80 cr:save()
81 if data[graph].border_color then
82 cr:translate(1, 1)
83 width, height = width - 2, height - 2
84 end
86 -- Draw a stacked graph
87 if data[graph].stack then
89 if data[graph].scale then
90 for _, v in ipairs(values) do
91 for __, sv in ipairs(v) do
92 if sv > max_value then
93 max_value = sv
94 end
95 end
96 end
97 end
99 for i = 0, width do
100 local rel_i = 0
101 local rel_x = i + 0.5
103 if data[graph].stack_colors then
104 for idx, col in ipairs(data[graph].stack_colors) do
105 local stack_values = values[idx]
106 if stack_values and i < #stack_values then
107 local value = stack_values[#stack_values - i] + rel_i
108 cr:move_to(rel_x, height * (1 - (rel_i / max_value)))
109 cr:line_to(rel_x, height * (1 - (value / max_value)))
110 cr:set_source(color(col or "#ff0000"))
111 cr:stroke()
112 rel_i = value
117 else
118 if data[graph].scale then
119 for _, v in ipairs(values) do
120 if v > max_value then
121 max_value = v
126 -- Draw the background on no value
127 if #values ~= 0 then
128 -- Draw reverse
129 for i = 0, #values - 1 do
130 local value = values[#values - i]
131 if value >= 0 then
132 value = value / max_value
133 cr:move_to(i + 0.5, height * (1 - value))
134 cr:line_to(i + 0.5, height)
137 cr:set_source(color(data[graph].color or "#ff0000"))
138 cr:stroke()
143 -- Undo the cr:translate() for the border
144 cr:restore()
146 -- Draw the border last so that it overlaps already drawn values
147 if data[graph].border_color then
148 -- We decremented these by two above
149 width, height = width + 2, height + 2
151 -- Draw the border
152 cr:rectangle(0.5, 0.5, width - 1, height - 1)
153 cr:set_source(color(data[graph].border_color or "#ffffff"))
154 cr:stroke()
158 function fit(graph, width, height)
159 return data[graph].width, data[graph].height
162 --- Add a value to the graph
163 -- @param graph The graph.
164 -- @param value The value between 0 and 1.
165 -- @param group The stack color group index.
166 local function add_value(graph, value, group)
167 if not graph then return end
169 local value = value or 0
170 local values = data[graph].values
171 local max_value = data[graph].max_value
172 value = math.max(0, value)
173 if not data[graph].scale then
174 value = math.min(max_value, value)
177 if data[graph].stack and group then
178 if not data[graph].values[group]
179 or type(data[graph].values[group]) ~= "table"
180 then
181 data[graph].values[group] = {}
183 values = data[graph].values[group]
185 table.insert(values, value)
187 local border_width = 0
188 if data[graph].border_color then border_width = 2 end
190 -- Ensure we never have more data than we can draw
191 while #values > data[graph].width - border_width do
192 table.remove(values, 1)
195 graph:emit_signal("widget::updated")
196 return graph
200 --- Set the graph height.
201 -- @param graph The graph.
202 -- @param height The height to set.
203 function set_height(graph, height)
204 if height >= 5 then
205 data[graph].height = height
206 graph:emit_signal("widget::updated")
208 return graph
211 --- Set the graph width.
212 -- @param graph The graph.
213 -- @param width The width to set.
214 function set_width(graph, width)
215 if width >= 5 then
216 data[graph].width = width
217 graph:emit_signal("widget::updated")
219 return graph
222 -- Build properties function
223 for _, prop in ipairs(properties) do
224 if not _M["set_" .. prop] then
225 _M["set_" .. prop] = function(graph, value)
226 data[graph][prop] = value
227 graph:emit_signal("widget::updated")
228 return graph
233 --- Create a graph widget.
234 -- @param args Standard widget() arguments. You should add width and height
235 -- key to set graph geometry.
236 -- @return A graph widget.
237 function new(args)
238 local args = args or {}
240 local width = args.width or 100
241 local height = args.height or 20
243 if width < 5 or height < 5 then return end
245 local graph = base.make_widget()
247 data[graph] = { width = width, height = height, values = {}, max_value = 1 }
249 -- Set methods
250 graph.add_value = add_value
251 graph.draw = draw
252 graph.fit = fit
254 for _, prop in ipairs(properties) do
255 graph["set_" .. prop] = _M["set_" .. prop]
258 return graph
261 setmetatable(_M, { __call = function(_, ...) return new(...) end })
263 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80