wibox: setvisible checks for screen
[awesome.git] / lib / naughty.lua.in
blobdb688d526647292b6c4a3d9e45fefcb04c0796a4
1 ----------------------------------------------------------------------------
2 -- @author koniu <gkusnierz@gmail.com>
3 -- @copyright 2008 koniu
4 -- @release @AWESOME_VERSION@
5 ----------------------------------------------------------------------------
7 -- Package environment
8 local pairs = pairs
9 local table = table
10 local wibox = wibox
11 local image = image
12 local hooks = require("awful.hooks")
13 local string = string
14 local widget = widget
15 local button = button
16 local capi = { screen = screen }
17 local bt = require("beautiful")
18 local screen = screen
20 --- Notification library
21 module("naughty")
23 --- Naughty configuration - a table containing common/default popup settings.
24 -- You can override some of these for individual popups using args to notify().
25 -- @name config
26 -- @field timeout Number of seconds after which popups disappear.
27 -- Set to 0 for no timeout. Default: 5
28 -- @field screen Screen on which the popups will appear number. Default: 1
29 -- @field position Corner of the workarea the popups will appear.
30 -- Valid values: 'top_right', 'top_left', 'bottom_right', 'bottom_left'.
31 -- Default: 'top_right'
32 -- @field padding Space between popups and edge of the workarea. Default: 4
33 -- @field height Height of a single line of text. Default: 16
34 -- @field width Width of a popup. Default: 300
35 -- @field spacing Spacing between popups. Default: 1
36 -- @field ontop Boolean forcing popups to display on top. Default: true
37 -- @field margin Space between popup edge and content. Default: 10
38 -- @field font Popup font. Default: beautiful.font or "Verdana 8"
39 -- @field icon Popup icon. Default: nil
40 -- @field icon_size Size of the icon in pixels. Default: 16
41 -- @field fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
42 -- @field bg Background color. Default: beautiful.bg_focus or '#535d6c'
43 -- @field border_color Border color.
44 -- Default: beautiful.border_focus or '#535d6c'
45 -- @field border_width Border width. Default: 1
46 -- @field hover_timeout Delay in seconds after which hovered popup disappears.
47 -- Default: nil
48 -- @class table
50 config = {}
51 config.timeout = 5
52 config.screen = 1
53 config.position = "top_right"
54 config.padding = 4
55 config.height = 16
56 config.width = 300
57 config.spacing = 1
58 config.ontop = true
59 config.margin = 10
60 config.icon = nil
61 config.icon_size = 16
62 config.border_width = 1
63 config.hover_timeout = nil
65 --- Index of notifications. See config table for valid 'position' values.
66 -- Each element is a table consisting of:
67 -- @field box Wibox object containing the popup
68 -- @field height Popup height
69 -- @field width Popup width
70 -- @field die Function to be executed on timeout
71 -- @name notifications[position]
72 -- @class table
74 notifications = {}
75 for s = 1, screen.count() do
76 notifications[s] = {
77 top_left = {},
78 top_right = {},
79 bottom_left = {},
80 bottom_right = {},
82 end
84 --- Evaluate desired position of the notification by index - internal
85 -- @param idx Index of the notification
86 -- @param position top_right | top_left | bottom_right | bottom_left
87 -- @param height Popup height
88 -- @param width Popup width (optional)
89 -- @return Absolute position in {x, y} dictionary
91 local function get_offset(screen, position, idx, width, height)
92 local ws = capi.screen[screen].workarea
93 local v = {}
94 width = width or notifications[screen][position][idx].width or config.width
96 -- calculate x
97 if position:match("left") then
98 v.x = ws.x + config.padding
99 else
100 v.x = ws.x + ws.width - (width + config.border_width*2 + config.padding)
103 -- calculate existing popups' height
104 local existing = 0
105 for i = 1, idx-1, 1 do
106 existing = existing + notifications[screen][position][i].height + config.spacing + config.border_width*2
109 -- calculate y
110 if position:match("top") then
111 v.y = ws.y + config.padding + existing
112 else
113 v.y = ws.y + ws.height - (config.padding + config.border_width + height + existing)
116 -- if positioned outside workarea, destroy oldest popup and recalculate
117 if v.y + height > ws.y + ws.height or v.y < ws.y then
118 idx = idx - 1
119 destroy(notifications[screen][position][1])
120 v = get_offset(screen, position, idx, width, height)
123 return v
126 --- Re-arrange notifications according to their position and index - internal
127 -- @return None
128 local function arrange(screen)
129 for p,pos in pairs(notifications[screen]) do
130 for i,notification in pairs(notifications[screen][p]) do
131 local offset = get_offset(screen, p, i, notification.width, notification.height)
132 notification.box:geometry({ x = offset.x, y = offset.y, width = notification.width, height = notification.height })
133 notification.idx = i
138 --- Destroy notification by index
139 -- @param notification Notification object to be destroyed
140 -- @return True if the popup was successfully destroyed, nil otherwise
141 function destroy(notification)
142 if notification then
143 local scr = notification.box.screen
144 table.remove(notifications[notification.box.screen][notification.position], notification.idx)
145 hooks.timer.unregister(notification.die)
146 notification.box.screen = nil
147 arrange(scr)
148 return true
152 --- Create notification. args is a dictionary of optional arguments. For more information and defaults see respective fields in config table.
153 -- @param text Text of the notification
154 -- @param timeout Time in seconds after which popup expires
155 -- @param title Title of the notification
156 -- @param position Corner of the workarea the popups will appear
157 -- @param icon Path to icon
158 -- @param icon_size Desired icon size in px
159 -- @param fg Foreground color
160 -- @param bg Background color
161 -- @param screen Target screen for the notification
162 -- @param ontop Target screen for the notification
163 -- @param run Function to run on left click
164 -- @param width The popup width
165 -- @usage naughty.notify({ title = 'Achtung!', text = 'You\'re idling', timeout = 0 })
166 -- @return The notification object
167 function notify(args)
168 -- gather variables together
169 local timeout = args.timeout or config.timeout
170 local icon = args.icon or config.icon
171 local icon_size = args.icon_size or config.icon_size
172 local text = args.text or ""
173 local screen = args.screen or config.screen
174 local ontop = args.ontop or config.ontop
175 local width = args.width or config.width
177 -- beautiful
178 local beautiful = bt.get()
179 local font = args.font or config.font or beautiful.font or "Verdana 8"
180 local fg = args.fg or config.fg or beautiful.fg_normal or '#ffffff'
181 local bg = args.bg or config.bg or beautiful.bg_normal or '#535d6c'
182 local border_color = config.border_color or beautiful.bg_focus or '#535d6c'
183 local notification = {}
184 notification.position = args.position or config.position
185 notification.idx = #notifications[screen][notification.position] + 1
187 local title = ""
188 if args.title then title = args.title .. "\n" end
190 -- hook destroy
191 local die = function () destroy(notification) end
192 hooks.timer.register(timeout, die)
193 notification.die = die
195 local run = args.run or die
197 local hover_destroy = function ()
198 if config.hover_timeout == 0 then die()
199 else hooks.timer.register(config.hover_timeout, die) end
202 -- create textbox
203 local textbox = widget({ type = "textbox", name = "text", align = "flex" })
204 textbox:buttons({ button({ }, 1, run),
205 button({ }, 3, die) })
206 textbox.text = string.format('<margin right="'..config.margin..'" left="'..config.margin..'"/><span font_desc="%s"><b>%s</b>%s</span>', font, title, text)
207 if config.hover_timeout then textbox.mouse_enter = hover_destroy end
209 -- create iconbox
210 local iconbox = nil
211 if icon then
212 iconbox = widget({ type = "imagebox", name = "icon", align = "left" })
213 iconbox:buttons({ button({ }, 1, run),
214 button({ }, 3, die) })
215 local img = image(icon)
216 if icon_size then
217 img = img:crop_and_scale(0,0,img.height,img.width,icon_size,icon_size)
218 iconbox.resize = false
220 iconbox.image = img
221 if config.hover_timeout then iconbox.mouse_enter = hover_destroy end
224 -- create container wibox
225 notification.box = wibox({ name = "not" .. notification.idx,
226 position = "floating",
227 fg = fg,
228 bg = bg,
229 border_color = config.border_color,
230 border_width = config.border_width })
232 -- position the wibox
233 local lines = 1; for i in string.gmatch(title..text, "\n") do lines = lines + 1 end
234 if iconbox and iconbox.image.height > lines * config.height then
235 notification.height = iconbox.image.height
236 else
237 notification.height = lines * config.height end
238 notification.width = width
239 local offset = get_offset(screen, notification.position, notification.idx, notification.width, notification.height)
240 notification.box:geometry({ width = notification.width,
241 height = notification.height,
242 x = offset.x,
243 y = offset.y })
244 notification.box.ontop = ontop
245 notification.box.screen = screen
247 -- populate widgets
248 notification.box.widgets = { iconbox, textbox }
250 -- insert the notification to the table
251 table.insert(notifications[screen][notification.position], notification)
253 -- return the notification
254 return notification
256 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80