lua: color pushlstring gets real len
[awesome.git] / lib / naughty.lua.in
blobfdf9c2487b57451e5efbf1a8d8cab552403b1130
1 ----------------------------------------------------------------------------
2 -- @author koniu <gkusnierz@gmail.com>
3 -- @copyright 2008 koniu
4 -- @release @AWESOME_VERSION@
5 ----------------------------------------------------------------------------
6 --
7 -- Usage:
8 --
9 -- require("naughty")
10 -- naughty.notify({ text = "notification",
11 -- title = "title",
12 -- position = "top_left"|"top_right"|"bottom_left"|"bottom_right",
13 -- timeout = 5,
14 -- icon="/path/to/image",
15 -- fg="#ffggcc",
16 -- bg="#bbggcc",
17 -- screen = 1 })
19 -- Package environment
20 local pairs = pairs
21 local table = table
22 local wibox = wibox
23 local image = image
24 local hooks = require("awful.hooks")
25 local string = string
26 local widget = widget
27 local button = button
28 local capi = { screen = screen }
29 local bt = require("awful.beautiful")
30 local beautiful = bt.get()
32 --- Notification library
33 module("naughty")
35 --- Naughty configuration - a table containing common/default popup settings.
36 -- You can override some of these for individual popups using args to notify().
37 -- @name config
38 -- @field timeout Number of seconds after which popups disappear.
39 -- Set to 0 for no timeout. Default: 5
40 -- @field screen Screen on which the popups will appear number. Default: 1
41 -- @field position Corner of the workarea the popups will appear.
42 -- Valid values: 'top_right', 'top_left', 'bottom_right', 'bottom_left'.
43 -- Default: 'top_right'
44 -- @field margin Space between popups and edge of the workarea. Default: 4
45 -- @field height Height of a single-line popup. Default: 16
46 -- @field width Width of a popup. Default: 300
47 -- @field gap Spacing between popups. Default: 1
48 -- @field ontop Boolean forcing popups to display on top. Default: true
49 -- @field font Popup font. Default: beautiful.font or "Verdana 8"
50 -- @field icon Popup icon. Default: nil
51 -- @field fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
52 -- @field bg Background color. Default: beautiful.bg_focus or '#535d6c'
53 -- @field border_color Border color.
54 -- Default: beautiful.border_focus or '#535d6c'
55 -- @field border_width Border width. Default: 1
56 -- @class table
58 config = {}
59 config.timeout = 5
60 config.screen = 1
61 config.position = "top_right"
62 config.margin = 4
63 config.height = 16
64 config.width = 300
65 config.gap = 1
66 config.ontop = true
67 config.font = beautiful.font or "Verdana 8"
68 config.icon = nil
69 config.fg = beautiful.fg_focus or '#ffffff'
70 config.bg = beautiful.bg_focus or '#535d6c'
71 config.border_color = beautiful.border_focus or '#535d6c'
72 config.border_width = 1
74 --- Index of notifications. See config table for valid 'position' values.
75 -- Each element is a table consisting of:
76 -- @field box Wibox object containing the popup
77 -- @field lines Number of lines in title..text
78 -- @field timer Function to be executed on timeout
79 -- @name notifications[position]
80 -- @class table
82 notifications = {
83 top_left = {},
84 top_right = {},
85 bottom_left = {},
86 bottom_right = {},
89 local ws = capi.screen[config.screen].workarea
90 local ss = capi.screen[config.screen].geometry
92 --- Evaluate desired position of the notification by index - internal
93 -- @param idx Index of the notification
94 -- @param position top_right | top_left | bottom_right | bottom_left
95 -- @param lines Number of text lines in the notification
96 -- @return Absolute position in {x, y} dictionary
98 local function get_offset(idx, position, lines)
99 local v = {}
101 -- calculate x
102 if position:match("left") then
103 v.x = ws.x + config.margin
104 else
105 v.x = ws.x + ws.width - (config.width + config.border_width*2 + config.margin)
108 -- calculate existing popups' height
109 local existing = 0
110 for i = 1, idx-1, 1 do
111 existing = existing + config.height*notifications[position][i].lines + config.gap + config.border_width*2
114 -- calculate y
115 if position:match("top") then
116 v.y = ws.y + config.margin + existing
117 else
118 v.y = ws.y + ws.height - (config.margin + config.border_width + config.height*lines + existing)
121 return v
124 --- Re-arrange notifications according to their position and index - internal
125 -- @return None
126 local function arrange()
127 for p,pos in pairs(notifications) do
128 for i,notification in pairs(notifications[p]) do
129 local offset = get_offset(i, p, notification.lines)
130 notification.box:geometry({ x = offset.x, y = offset.y, width = config.width, height = notification.lines * config.height })
131 notification.idx = i
136 --- Destroy notification by index
137 -- @param idx Index of the notification
138 -- @param position One of 4 keys in notification dictionary: top_right, top_left, bottom_right, bottom_left
139 -- @return True if the popup was successfully destroyed, nil otherwise
140 function destroy(notification)
141 if notification then
142 notification.box.screen = nil
143 hooks.timer.unregister(notification.timer)
144 table.remove(notifications[notification.position], notification.idx)
145 arrange()
146 return true
150 --- Create notification. args is a dictionary of optional arguments. For more information and defaults see respective fields in config table.
151 -- @param text Text of the notification
152 -- @param timeout Time in seconds after which popup expires
153 -- @param title Title of the notification
154 -- @param position Corner of the workarea the popups will appear
155 -- @param icon Path to icon
156 -- @param fg Foreground color
157 -- @param bg Background color
158 -- @param screen Target screen for the notification
159 -- @param ontop Target screen for the notification
160 -- @usage naughty.notify({ title = 'Achtung!', text = 'You\'re idling', timeout = 0 })
161 function notify(args)
162 -- gather settings together
163 local timeout = args.timeout or config.timeout
164 local position = args.position or config.position
165 local icon = args.icon or config.icon
166 local text = args.text or ""
167 local screen = args.screen or config.screen
168 local ontop = args.ontop or config.ontop
170 local title = ""
171 if args.title then
172 title = " " .. args.title .. "\n"
175 local lines = 1
176 for i in string.gmatch(title..text, "\n") do
177 lines = lines + 1
180 -- create the container wibox
181 local idx = #notifications[position] + 1
182 local box = wibox({ name = "not" .. idx,
183 position = "floating",
184 fg = args.fg or config.fg,
185 bg = args.bg or config.bg,
186 border_color = config.border_color,
187 border_width = config.border_width })
189 -- position the wibox
190 local offset = get_offset(idx, position, lines)
191 box:geometry({ width = config.width,
192 height = config.height * lines,
193 x = offset.x,
194 y = offset.y })
196 box.ontop = ontop
197 box.screen = screen
199 local notification = {
200 box = box,
201 lines = lines,
202 position = position,
203 idx = idx
206 -- populate the wibox with widgets
207 local textbox = widget({ type = "textbox", name = "text", align = "flex" })
208 textbox:buttons({ button({ }, 1, function () destroy(notification) end) })
209 textbox.text = string.format('<span font_desc="%s"><b>%s</b> %s</span>',
210 config.font, title, text)
212 local iconbox = nil
213 if icon then
214 iconbox = widget({ type = "imagebox", name = "icon", align = "left" })
215 iconbox:buttons({ button({ }, 1, function () destroy(notification) end) })
216 iconbox.image = image(icon)
217 iconbox.width = 20
220 box.widgets = { iconbox, textbox }
222 local timer = function () destroy(notification) end
223 hooks.timer.register(timeout, timer)
224 notification.timer = timer
226 -- insert the notification to the table
227 table.insert(notifications[position],notification)
230 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80