1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2009 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 local setmetatable
= setmetatable
12 local color
= require("gears.color")
13 local base
= require("wibox.widget.base")
17 local graph
= { mt
= {} }
19 local data
= setmetatable({}, { __mode
= "k" })
21 --- Set the graph border color.
22 -- If the value is nil, no border will be drawn.
23 -- @name set_border_color
25 -- @param graph The graph.
26 -- @param color The border color to set.
28 --- Set the graph foreground color.
31 -- @param graph The graph.
32 -- @param color The graph color.
34 --- Set the graph background color.
35 -- @name set_background_color
37 -- @param graph The graph.
38 -- @param color The graph background color.
40 --- Set the maximum value the graph should handle.
41 -- If "scale" is also set, the graph never scales up below this value, but it
42 -- automatically scales down to make all data fit.
43 -- @name set_max_value
45 -- @param graph The graph.
46 -- @param value The value.
48 --- Set the graph to automatically scale its values. Default is false.
51 -- @param graph The graph.
52 -- @param scale A boolean value
54 --- Set the graph to draw stacks. Default is false.
57 -- @param graph The graph.
58 -- @param stack A boolean value.
60 --- Set the graph stacking colors. Order matters.
61 -- @name set_stack_colors
63 -- @param graph The graph.
64 -- @param stack_colors A table with stacking colors.
66 local properties
= { "width", "height", "border_color", "stack",
67 "stack_colors", "color", "background_color",
68 "max_value", "scale" }
70 function graph
.draw(_graph
, wibox
, cr
, width
, height
)
71 local max_value
= data
[_graph
].max_value
72 local values
= data
[_graph
].values
76 -- Draw the background first
77 cr
:set_source(color(data
[_graph
].background_color
or "#000000aa"))
80 -- Account for the border width
82 if data
[_graph
].border_color
then
84 width
, height
= width
- 2, height
- 2
87 -- Draw a stacked graph
88 if data
[_graph
].stack
then
90 if data
[_graph
].scale
then
91 for _
, v
in ipairs(values
) do
92 for __
, sv
in ipairs(v
) do
93 if sv
> max_value
then
102 local rel_x
= i
+ 0.5
104 if data
[_graph
].stack_colors
then
105 for idx
, col
in ipairs(data
[_graph
].stack_colors
) do
106 local stack_values
= values
[idx
]
107 if stack_values
and i
< #stack_values
then
108 local value
= stack_values
[#stack_values
- i
] + rel_i
109 cr
:move_to(rel_x
, height
* (1 - (rel_i
/ max_value
)))
110 cr
:line_to(rel_x
, height
* (1 - (value
/ max_value
)))
111 cr
:set_source(color(col
or "#ff0000"))
119 if data
[_graph
].scale
then
120 for _
, v
in ipairs(values
) do
121 if v
> max_value
then
127 -- Draw the background on no value
130 for i
= 0, #values
- 1 do
131 local value
= values
[#values
- i
]
133 value
= value
/ max_value
134 cr
:move_to(i
+ 0.5, height
* (1 - value
))
135 cr
:line_to(i
+ 0.5, height
)
138 cr
:set_source(color(data
[_graph
].color
or "#ff0000"))
144 -- Undo the cr:translate() for the border
147 -- Draw the border last so that it overlaps already drawn values
148 if data
[_graph
].border_color
then
149 -- We decremented these by two above
150 width
, height
= width
+ 2, height
+ 2
153 cr
:rectangle(0.5, 0.5, width
- 1, height
- 1)
154 cr
:set_source(color(data
[_graph
].border_color
or "#ffffff"))
159 function graph
.fit(_graph
, width
, height
)
160 return data
[_graph
].width
, data
[_graph
].height
163 --- Add a value to the graph
164 -- @param _graph The graph.
165 -- @param value The value between 0 and 1.
166 -- @param group The stack color group index.
167 local function add_value(_graph
, value
, group
)
168 if not _graph
then return end
170 local value
= value
or 0
171 local values
= data
[_graph
].values
172 local max_value
= data
[_graph
].max_value
173 value
= math
.max(0, value
)
174 if not data
[_graph
].scale
then
175 value
= math
.min(max_value
, value
)
178 if data
[_graph
].stack
and group
then
179 if not data
[_graph
].values
[group
]
180 or type(data
[_graph
].values
[group
]) ~= "table"
182 data
[_graph
].values
[group
] = {}
184 values
= data
[_graph
].values
[group
]
186 table.insert(values
, value
)
188 local border_width
= 0
189 if data
[_graph
].border_color
then border_width
= 2 end
191 -- Ensure we never have more data than we can draw
192 while #values
> data
[_graph
].width
- border_width
do
193 table.remove(values
, 1)
196 _graph
:emit_signal("widget::updated")
201 --- Set the graph height.
202 -- @param height The height to set.
203 function graph
:set_height(height
)
205 data
[self
].height
= height
206 self
:emit_signal("widget::updated")
211 --- Set the graph width.
212 -- @param graph The graph.
213 -- @param width The width to set.
214 function graph
:set_width(width
)
216 data
[self
].width
= width
217 self
:emit_signal("widget::updated")
222 -- Build properties function
223 for _
, prop
in ipairs(properties
) do
224 if not graph
["set_" .. prop
] then
225 graph
["set_" .. prop
] = function(_graph
, value
)
226 data
[_graph
][prop
] = value
227 _graph
:emit_signal("widget::updated")
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 graph
.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 }
250 _graph
.add_value
= add_value
251 _graph
.draw
= graph
.draw
252 _graph
.fit
= graph
.fit
254 for _
, prop
in ipairs(properties
) do
255 _graph
["set_" .. prop
] = graph
["set_" .. prop
]
261 function graph
.mt
:__call(...)
262 return graph
.new(...)
265 return setmetatable(graph
, graph
.mt
)
267 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80