123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- -- @author cedlemo
- local setmetatable = setmetatable
- local ipairs = ipairs
- local math = math
- local table = table
- local type = type
- local string = string
- local color = require("gears.color")
- local base = require("wibox.widget.base")
- local helpers = require("blingbling.helpers")
- local superproperties = require('blingbling.superproperties')
- ---Graph widget.
- --@module blingbling.linegraph
- local linegraph = { mt = {} }
- local data = setmetatable({}, { __mode = "k" })
- ---Fill all the widget (width * height) with this color (default is none ).
- --@usage mygraph:set_background_color(string) -->"#rrggbbaa"
- --@name set_background_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb"
- ---Fill the graph area background with this color (default is none).
- --@usage mygraph:set_graph_background_color(string) -->"#rrggbbaa"
- --@name set_graph_background_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb"
- ---Set rounded corners for background and graph background.
- --@usage mygraph:set_rounded_size(a) -> a in [0,1]
- --@name set_rounded_size
- --@class function
- --@param rounded_size float in [0,1]
- ---Define the top and bottom margin for the graph area.
- --@usage mygraph:set_v_margin(integer)
- --@name set_v_margin
- --@class function
- --@param margin an integer for top and bottom margin
- ---Define the left and right margin for the graph area.
- --@usage mygraph:set_h_margin(integer)
- --@name set_h_margin
- --@class function
- --@param margin an integer for left and right margin
- ---Define the graph color.
- --@usage mygraph:set_graph_color(string) -->"#rrggbbaa"
- --@name set_graph_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb"
- ---Define the graph outline.
- --@usage mygraph:set_graph_line_color(string) -->"#rrggbbaa"
- --@name set_graph_line_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb"
- ---Display text on the graph or not.
- --@usage mygraph:set_show_text(boolean) --> true or false
- --@name set_show_text
- --@class function
- --@param boolean true or false (default is false)
- ---Define displayed text value format string
- --@usage mygraph:set_value_format(string) --> "%2.f"
- --@name set_value_format
- --@class function
- --@param graph the graph
- --@param printf format string for display text
- ---Define the color of the text.
- --@usage mygraph:set_text_color(string) -->"#rrggbbaa"
- --@name set_text_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb" defaul is white
- ---Define the background color of the text.
- --@usage mygraph:set_text_background_color(string) -->"#rrggbbaa"
- --@name set_text_background_color
- --@class function
- --@param color a string "#rrggbbaa" or "#rrggbb"
- ---Define the text's font .
- --@usage mygraph:set_font(string)
- --@name set_font
- --@class function
- --@param font a string that contains the font name family and weight
- ---Define the text font size.
- --@usage mygraph:set_font_size(integer)
- --@name set_font_size
- --@class function
- --@param size the font size
- ---Define the template of the text to display.
- --@usage mygraph:set_label(string)
- --By default the text is : (value_send_to_the_widget *100) .. "%"
- --static string: example set_label("CPU usage:") will display "CUP usage:" on the graph
- --dynamic string: use $percent in the string example set_label("Load $percent %") will display "Load 10%"
- --@name set_label
- --@class function
- --@param text the text to display
- local properties = { "width", "height", "h_margin", "v_margin",
- "background_color", "graph_background_color",
- "rounded_size", "graph_color", "graph_line_color",
- "show_text", "text_color", "font_size", "font",
- "text_background_color", "label", "value_format"
- }
- function linegraph.draw(graph, wibox, cr, width, height)
- -- Set the values we need
- local props = helpers.load_properties(properties, data, graph, superproperties)
- local line_width = 1
- cr:set_line_width(line_width)
- cr:set_antialias("subpixel")
- -- Draw the widget background
- helpers.draw_rounded_corners_rectangle( cr,
- 0,
- 0,
- width,
- height,
- props.background_color,
- props.rounded_size
- )
- -- Draw the graph background
- helpers.draw_rounded_corners_rectangle(cr,
- props.h_margin,
- props.v_margin,
- width - props.h_margin,
- height - props.v_margin ,
- props.graph_background_color,
- props.rounded_size,
- props.graph_background_border)
- helpers.clip_rounded_corners_rectangle(cr,
- props.h_margin,
- props.v_margin,
- width - props.h_margin,
- height - (props.v_margin),
- props.rounded_size
- )
- --Drawn the graph
- --if graph_background_border is set, graph must not be drawn on it
- --find nb values we can draw every column_length px
- --if rounded, make sure that graph don't begin or end outside background
- --check for the less value between hight and height to calculate the space for rounded size:
- local column_length = 6
- local less_value = height > width and width / 2 or height / 2
- local used_width = width - (2 * props.h_margin + 2 * (props.rounded_size * less_value))
- max_column = math.ceil( used_width / column_length)
- --Check if the table graph values is empty / not initialized
- --if next(data[graph].values) == nil then
- if #data[graph].values == 0 or #data[graph].values ~= max_column then
- -- initialize graph_values with empty values:
- data[graph].values={}
- for i=1,max_column do
- --the following line feed the graph with random value if you uncomment it and comment the line after it
- --data[graph].values[i]=math.random(0,100) / 100
- data[graph].values[i] = 0
- end
- end
- local values = data[graph].values
- --Fill the graph
- x = width -(props.h_margin + props.rounded_size * less_value)
- y = height-(props.v_margin)
- cr:new_path()
- cr:move_to(x,y)
- cr:line_to(x,y)
- for i=1,max_column do
- y_range = height - (2 * props.v_margin)
- y = height - (props.v_margin + ((values[i]) * y_range))
- cr:line_to(x,y)
- x = x-column_length
- end
- y = height - (props.v_margin )
- cr:line_to(x + column_length ,y)
- cr:line_to(width - props.h_margin,height - (props.v_margin ))
- cr:close_path()
- local r,g,b,a = helpers.hexadecimal_to_rgba_percent(props.graph_color)
- cr:set_source_rgba(r, g, b, a)
- cr:fill()
- --Draw the graph line
- x = width - (props.h_margin + props.rounded_size * less_value)
- y = height - (props.v_margin)
- cr:new_path()
- cr:move_to(x,y)
- cr:line_to(x,y)
- for i=1,max_column do
- y_range = height - (2 * props.v_margin)
- y = height - (props.v_margin + ((values[i]) * y_range))
- -- this is a trick:
- -- when value a equal to zero, I remove add 1 pixel to the y point in order
- -- to put the point outside the clip area done with helpers.clip_rounded_corners_rectangle
- -- so when a value is == 0 the point is almost not visible whereas the line of the graph
- -- is not broken.
- if values[i] == 0 then
- y = y + 1
- end
- cr:line_to(x,y)
- x = x - column_length
- end
- y = height - (props.v_margin - 1) -- the y point here is set outside the clip rectangle
- cr:line_to(x + column_length ,y)
- r,g,b,a = helpers.hexadecimal_to_rgba_percent(props.graph_line_color)
- cr:set_source_rgba(r, g, b, a)
- cr:stroke()
- if props.show_text == true then
- --Draw Text and it's background
- local font
- if type(props.font) == "string" then
- font = props.font .. " " .. props.font_size
- elseif type(props.font) == "table" then
- font = (props.font.family or "Sans") .. " " .. (props.font.slang or "normal") .. " " .. (props.font.weight or "normal") .. " " .. props.font_size
- end
- local value = string.format(props.value_format, values[1] * 100)
- if props.label then
- text=string.gsub(data[graph].label,"$percent", value)
- else
- text=value .. "%"
- end
- helpers.draw_layout_and_background(cr,
- text,
- props.h_margin + props.rounded_size * less_value,
- height/2 ,
- font,
- props.text_background_color,
- props.text_color,
- "start",
- "middle")
- end
- end
- function linegraph.fit(graph, width, height)
- return data[graph].width, data[graph].height
- end
- ---Add a value to the graph.
- --For compatibility between old and new awesome widget, add_value can be replaced by set_value
- --@usage mygraph:add_value(a) or mygraph:set_value(a)
- --@param graph the graph
- --@param value The value between 0 and 1.
- --@param group The stack color group index.
- local function add_value(graph, value, group)
- if not graph then return end
- local value = value or 0
- local values = data[graph].values
- if string.find(value, "nan") then
- value=0
- end
- local values = data[graph].values
- table.remove(values, #values)
- table.insert(values,1,value)
- graph:emit_signal("widget::updated")
- return graph
- end
- ---Set the graph height.
- --@param height The height to set.
- function linegraph:set_height( height)
- if height >= 5 then
- data[self].height = height
- self:emit_signal("widget::updated")
- end
- return self
- end
- ---Set the graph width.
- --@param width The width to set.
- function linegraph:set_width( width)
- if width >= 5 then
- data[self].width = width
- self:emit_signal("widget::updated")
- end
- return self
- end
- -- Build properties function
- for _, prop in ipairs(properties) do
- if not linegraph["set_" .. prop] then
- linegraph["set_" .. prop] = function(graph, value)
- data[graph][prop] = value
- graph:emit_signal("widget::updated")
- return graph
- end
- end
- end
- ---Create a graph widget.
- --@param args Standard widget() arguments. You should add width and height
- --key to set graph geometry.
- --@return A graph widget.
- function linegraph.new(args)
- local args = args or {}
- args.width = args.width or 100
- args.height = args.height or 20
- if args.width < 5 or args.height < 5 then return end
- local graph = base.make_widget()
- data[graph] = {}
- for _, v in ipairs(properties) do
- data[graph][v] = args[v]
- end
- data[graph].values = {}
- -- Set methods
- graph.set_value = add_value
- graph.add_value = add_value
- graph.draw = linegraph.draw
- graph.fit = linegraph.fit
- for _, prop in ipairs(properties) do
- graph["set_" .. prop] = linegraph["set_" .. prop]
- end
- return graph
- end
- function linegraph.mt:__call(...)
- return linegraph.new(...)
- end
- return setmetatable(linegraph, linegraph.mt)
|