1 ----------------------------------------------------------------------------
2 -- @author koniu <gkusnierz@gmail.com>
3 -- @copyright 2008 koniu
4 -- @release @AWESOME_VERSION@
5 ----------------------------------------------------------------------------
10 -- naughty.notify({ text = "notification",
12 -- position = "top_left"|"top_right"|"bottom_left"|"bottom_right",
14 -- icon="/path/to/image",
19 -- Package environment
24 local hooks
= require("awful.hooks")
28 local capi
= { screen
= screen
}
29 local bt
= require("awful.beautiful")
30 local beautiful
= bt
.get()
32 --- Notification library
35 --- Naughty configuration - a table containing common/default popup settings.
36 -- You can override some of these for individual popups using args to notify().
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 icon_size Size of the icon in pixels. Default: 16
52 -- @field fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
53 -- @field bg Background color. Default: beautiful.bg_focus or '#535d6c'
54 -- @field border_color Border color.
55 -- Default: beautiful.border_focus or '#535d6c'
56 -- @field border_width Border width. Default: 1
57 -- @field hover_timeout Delay in seconds after which hovered popup disappears.
64 config
.position
= "top_right"
70 config
.font
= beautiful
.font
or "Verdana 8"
73 config
.fg
= beautiful
.fg_focus
or '#ffffff'
74 config
.bg
= beautiful
.bg_focus
or '#535d6c'
75 config
.border_color
= beautiful
.border_focus
or '#535d6c'
76 config
.border_width
= 1
77 config
.hover_timeout
= nil
79 --- Index of notifications. See config table for valid 'position' values.
80 -- Each element is a table consisting of:
81 -- @field box Wibox object containing the popup
82 -- @field lines Number of lines in title..text
83 -- @field timer Function to be executed on timeout
84 -- @name notifications[position]
94 local ws
= capi
.screen
[config
.screen
].workarea
95 local ss
= capi
.screen
[config
.screen
].geometry
97 --- Evaluate desired position of the notification by index - internal
98 -- @param idx Index of the notification
99 -- @param position top_right | top_left | bottom_right | bottom_left
100 -- @param lines Number of text lines in the notification
101 -- @return Absolute position in {x, y} dictionary
103 local function get_offset(idx
, position
, lines
)
107 if position
:match("left") then
108 v
.x
= ws
.x
+ config
.margin
110 v
.x
= ws
.x
+ ws
.width
- (config
.width
+ config
.border_width
*2 + config
.margin
)
113 -- calculate existing popups' height
115 for i
= 1, idx
-1, 1 do
116 existing
= existing
+ config
.height
*notifications
[position
][i
].lines
+ config
.gap
+ config
.border_width
*2
120 if position
:match("top") then
121 v
.y
= ws
.y
+ config
.margin
+ existing
123 v
.y
= ws
.y
+ ws
.height
- (config
.margin
+ config
.border_width
+ config
.height
*lines
+ existing
)
126 -- if positioned outside workarea, destroy oldest popup and recalculate
127 if v
.y
+ config
.height
*lines
> ws
.y
+ ws
.height
or v
.y
< ws
.y
then
129 destroy(notifications
[position
][1])
130 v
= get_offset(idx
, position
, lines
)
136 --- Re-arrange notifications according to their position and index - internal
138 local function arrange()
139 for p
,pos
in pairs(notifications
) do
140 for i
,notification
in pairs(notifications
[p
]) do
141 local offset
= get_offset(i
, p
, notification
.lines
)
142 notification
.box
:geometry({ x
= offset
.x
, y
= offset
.y
, width
= config
.width
, height
= notification
.lines
* config
.height
})
148 --- Destroy notification by index
149 -- @param idx Index of the notification
150 -- @param position One of 4 keys in notification dictionary: top_right, top_left, bottom_right, bottom_left
151 -- @return True if the popup was successfully destroyed, nil otherwise
152 function destroy(notification
)
154 notification
.box
.screen
= nil
155 hooks
.timer
.unregister(notification
.timer
)
156 table.remove(notifications
[notification
.position
], notification
.idx
)
162 --- Create notification. args is a dictionary of optional arguments. For more information and defaults see respective fields in config table.
163 -- @param text Text of the notification
164 -- @param timeout Time in seconds after which popup expires
165 -- @param title Title of the notification
166 -- @param position Corner of the workarea the popups will appear
167 -- @param icon Path to icon
168 -- @param icon_size Desired icon size in px
169 -- @param fg Foreground color
170 -- @param bg Background color
171 -- @param screen Target screen for the notification
172 -- @param ontop Target screen for the notification
173 -- @param run Function to run on left click
174 -- @usage naughty.notify({ title = 'Achtung!', text = 'You\'re idling', timeout = 0 })
175 function notify(args
)
176 -- gather settings together
177 local timeout
= args
.timeout
or config
.timeout
178 local position
= args
.position
or config
.position
179 local icon
= args
.icon
or config
.icon
180 local icon_size
= args
.icon_size
or config
.icon_size
181 local text
= args
.text
or ""
182 local screen
= args
.screen
or config
.screen
183 local ontop
= args
.ontop
or config
.ontop
187 title
= " " .. args
.title
.. "\n"
191 for i
in string.gmatch(title
..text
, "\n") do
195 -- create the container wibox
196 local idx
= #notifications
[position
] + 1
197 local box
= wibox({ name
= "not" .. idx
,
198 position
= "floating",
199 fg
= args
.fg
or config
.fg
,
200 bg
= args
.bg
or config
.bg
,
201 border_color
= config
.border_color
,
202 border_width
= config
.border_width
})
204 -- position the wibox
205 local offset
= get_offset(idx
, position
, lines
)
206 box
:geometry({ width
= config
.width
,
207 height
= config
.height
* lines
,
214 local notification
= {
221 local die
= function () destroy(notification
) end
222 hooks
.timer
.register(timeout
, die
)
223 notification
.die
= die
225 local run
= args
.run
or die
227 local hover_destroy
= function ()
228 if config
.hover_timeout
== 0 then die()
229 else hooks
.timer
.register(config
.hover_timeout
, die
) end
232 -- populate the wibox with widgets
233 local textbox
= widget({ type = "textbox", name
= "text", align
= "flex" })
234 textbox
:buttons({ button({ }, 1, run
),
235 button({ }, 3, die
) })
236 textbox
.text
= string.format('<span font_desc="%s"><b>%s</b> %s</span>',
237 config
.font
, title
, text
)
238 if config
.hover_timeout
then textbox
.mouse_enter
= hover_destroy
end
242 iconbox
= widget({ type = "imagebox", name
= "icon", align
= "left" })
243 iconbox
:buttons({ button({ }, 1, run
),
244 button({ }, 3, die
) })
245 local img
= image(icon
)
247 img
= img
:crop_and_scale(0,0,img
.height
,img
.width
,icon_size
,icon_size
)
248 iconbox
.resize
= false
251 if config
.hover_timeout
then iconbox
.mouse_enter
= hover_destroy
end
254 box
.widgets
= { iconbox
, textbox
}
256 -- insert the notification to the table
257 table.insert(notifications
[position
],notification
)
260 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80