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")
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
24 -- @param graph The graph.
25 -- @param color The border color to set.
27 --- Set the graph foreground color.
30 -- @param graph The graph.
31 -- @param color The graph color.
33 --- Set the graph background color.
34 -- @name set_background_color
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
44 -- @param graph The graph.
45 -- @param value The value.
47 --- Set the graph to automatically scale its values. Default is false.
50 -- @param graph The graph.
51 -- @param scale A boolean value
53 --- Set the graph to draw stacks. Default is false.
56 -- @param graph The graph.
57 -- @param stack A boolean value.
59 --- Set the graph stacking colors. Order matters.
60 -- @name set_stack_colors
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 local border_width
= 0
74 if data
[graph
].border_color
then
80 -- Draw the background first
81 cr
:rectangle(border_width
, border_width
,
82 width
- (2 * border_width
),
83 height
- (2 * border_width
))
84 cr
:set_source(color(data
[graph
].background_color
or "#000000aa"))
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
100 for i
= 0, width
- (2 * border_width
) do
102 local rel_x
= border_width
+ 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
, border_width
+
110 (height
- 2 * border_width
) * (1 - (rel_i
/ max_value
)))
111 cr
:line_to(rel_x
, border_width
+
112 (height
- 2 * border_width
) * (1 - (value
/ max_value
)))
113 cr
:set_source(color(col
or "#ff0000"))
121 if data
[graph
].scale
then
122 for _
, v
in ipairs(values
) do
123 if v
> max_value
then
129 -- Draw the background on no value
132 for i
= 0, #values
- 1 do
133 local value
= values
[#values
- i
]
135 value
= value
/ max_value
136 cr
:move_to(border_width
+ i
+ 0.5, border_width
+
137 (height
- 2 * border_width
) * (1 - value
))
138 cr
:line_to(border_width
+ i
+ 0.5, border_width
+
139 (height
- 2 * border_width
))
142 cr
:set_source(color(data
[graph
].color
or "#ff0000"))
148 -- Draw the border last so that it overlaps already drawn values
149 if data
[graph
].border_color
then
151 cr
:rectangle(0.5, 0.5, width
- 1, height
- 1)
152 cr
:set_source(color(data
[graph
].border_color
or "#ffffff"))
157 function fit(graph
, width
, height
)
158 return data
[graph
].width
, data
[graph
].height
161 --- Add a value to the graph
162 -- @param graph The graph.
163 -- @param value The value between 0 and 1.
164 -- @param group The stack color group index.
165 local function add_value(graph
, value
, group
)
166 if not graph
then return end
168 local value
= value
or 0
169 local values
= data
[graph
].values
170 local max_value
= data
[graph
].max_value
171 value
= math
.max(0, value
)
172 if not data
[graph
].scale
then
173 value
= math
.min(max_value
, value
)
176 if data
[graph
].stack
and group
then
177 if not data
[graph
].values
[group
]
178 or type(data
[graph
].values
[group
]) ~= "table"
180 data
[graph
].values
[group
] = {}
182 values
= data
[graph
].values
[group
]
184 table.insert(values
, value
)
186 local border_width
= 0
187 if data
[graph
].border_color
then border_width
= 2 end
189 -- Ensure we never have more data than we can draw
190 while #values
> data
[graph
].width
- border_width
do
191 table.remove(values
, 1)
194 graph
:emit_signal("widget::updated")
199 --- Set the graph height.
200 -- @param graph The graph.
201 -- @param height The height to set.
202 function set_height(graph
, height
)
204 data
[graph
].height
= height
205 graph
:emit_signal("widget::updated")
210 --- Set the graph width.
211 -- @param graph The graph.
212 -- @param width The width to set.
213 function set_width(graph
, width
)
215 data
[graph
].width
= width
216 graph
:emit_signal("widget::updated")
221 -- Build properties function
222 for _
, prop
in ipairs(properties
) do
223 if not _M
["set_" .. prop
] then
224 _M
["set_" .. prop
] = function(graph
, value
)
225 data
[graph
][prop
] = value
226 graph
:emit_signal("widget::updated")
232 --- Create a graph widget.
233 -- @param args Standard widget() arguments. You should add width and height
234 -- key to set graph geometry.
235 -- @return A graph widget.
237 local args
= args
or {}
239 local width
= args
.width
or 100
240 local height
= args
.height
or 20
242 if width
< 5 or height
< 5 then return end
244 local graph
= base
.make_widget()
246 data
[graph
] = { width
= width
, height
= height
, values
= {}, max_value
= 1 }
249 graph
.add_value
= add_value
253 for _
, prop
in ipairs(properties
) do
254 graph
["set_" .. prop
] = _M
["set_" .. prop
]
260 setmetatable(_M
, { __call
= function(_
, ...) return new(...) end })
262 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80