1 ----------------------------------------------------------------------------
2 -- @author koniu <gkusnierz@gmail.com>
3 -- @copyright 2008 koniu
4 -- @release @AWESOME_VERSION@
5 ----------------------------------------------------------------------------
12 local hooks
= require("awful.hooks")
16 local capi
= { screen
= screen
}
17 local bt
= require("beautiful")
18 local beautiful
= bt
.get()
20 --- Notification library
23 --- Naughty configuration - a table containing common/default popup settings.
24 -- You can override some of these for individual popups using args to notify().
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 margin 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 gap Spacing between popups. Default: 1
36 -- @field ontop Boolean forcing popups to display on top. Default: true
37 -- @field font Popup font. Default: beautiful.font or "Verdana 8"
38 -- @field icon Popup icon. Default: nil
39 -- @field icon_size Size of the icon in pixels. Default: 16
40 -- @field fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
41 -- @field bg Background color. Default: beautiful.bg_focus or '#535d6c'
42 -- @field border_color Border color.
43 -- Default: beautiful.border_focus or '#535d6c'
44 -- @field border_width Border width. Default: 1
45 -- @field hover_timeout Delay in seconds after which hovered popup disappears.
52 config
.position
= "top_right"
58 config
.font
= beautiful
.font
or "Verdana 8"
61 config
.fg
= beautiful
.fg_focus
or '#ffffff'
62 config
.bg
= beautiful
.bg_focus
or '#535d6c'
63 config
.border_color
= beautiful
.border_focus
or '#535d6c'
64 config
.border_width
= 1
65 config
.hover_timeout
= nil
67 --- Index of notifications. See config table for valid 'position' values.
68 -- Each element is a table consisting of:
69 -- @field box Wibox object containing the popup
70 -- @field height Popup height
71 -- @field die Function to be executed on timeout
72 -- @name notifications[position]
82 --- Evaluate desired position of the notification by index - internal
83 -- @param idx Index of the notification
84 -- @param position top_right | top_left | bottom_right | bottom_left
85 -- @param height Popup height
86 -- @return Absolute position in {x, y} dictionary
88 local function get_offset(idx
, position
, height
)
89 local ws
= capi
.screen
[config
.screen
].workarea
93 if position
:match("left") then
94 v
.x
= ws
.x
+ config
.margin
96 v
.x
= ws
.x
+ ws
.width
- (config
.width
+ config
.border_width
*2 + config
.margin
)
99 -- calculate existing popups' height
101 for i
= 1, idx
-1, 1 do
102 existing
= existing
+ notifications
[position
][i
].height
+ config
.gap
+ config
.border_width
*2
106 if position
:match("top") then
107 v
.y
= ws
.y
+ config
.margin
+ existing
109 v
.y
= ws
.y
+ ws
.height
- (config
.margin
+ config
.border_width
+ height
+ existing
)
112 -- if positioned outside workarea, destroy oldest popup and recalculate
113 if v
.y
+ height
> ws
.y
+ ws
.height
or v
.y
< ws
.y
then
115 destroy(notifications
[position
][1])
116 v
= get_offset(idx
, position
, height
)
122 --- Re-arrange notifications according to their position and index - internal
124 local function arrange()
125 for p
,pos
in pairs(notifications
) do
126 for i
,notification
in pairs(notifications
[p
]) do
127 local offset
= get_offset(i
, p
, notification
.height
)
128 notification
.box
:geometry({ x
= offset
.x
, y
= offset
.y
, width
= config
.width
, height
= notification
.height
})
134 --- Destroy notification by index
135 -- @param idx Index of the notification
136 -- @param position One of 4 keys in notification dictionary: top_right, top_left, bottom_right, bottom_left
137 -- @return True if the popup was successfully destroyed, nil otherwise
138 function destroy(notification
)
140 notification
.box
.screen
= nil
141 hooks
.timer
.unregister(notification
.die
)
142 table.remove(notifications
[notification
.position
], notification
.idx
)
148 --- Create notification. args is a dictionary of optional arguments. For more information and defaults see respective fields in config table.
149 -- @param text Text of the notification
150 -- @param timeout Time in seconds after which popup expires
151 -- @param title Title of the notification
152 -- @param position Corner of the workarea the popups will appear
153 -- @param icon Path to icon
154 -- @param icon_size Desired icon size in px
155 -- @param fg Foreground color
156 -- @param bg Background color
157 -- @param screen Target screen for the notification
158 -- @param ontop Target screen for the notification
159 -- @param run Function to run on left click
160 -- @usage naughty.notify({ title = 'Achtung!', text = 'You\'re idling', timeout = 0 })
161 function notify(args
)
162 -- gather variables together
163 local timeout
= args
.timeout
or config
.timeout
164 local icon
= args
.icon
or config
.icon
165 local icon_size
= args
.icon_size
or config
.icon_size
166 local text
= args
.text
or ""
167 local screen
= args
.screen
or config
.screen
168 local ontop
= args
.ontop
or config
.ontop
170 local notification
= {}
171 notification
.position
= args
.position
or config
.position
172 notification
.idx
= #notifications
[notification
.position
] + 1
175 if args
.title
then title
= " " .. args
.title
.. "\n" end
178 local die
= function () destroy(notification
) end
179 hooks
.timer
.register(timeout
, die
)
180 notification
.die
= die
182 local run
= args
.run
or die
184 local hover_destroy
= function ()
185 if config
.hover_timeout
== 0 then die()
186 else hooks
.timer
.register(config
.hover_timeout
, die
) end
190 local textbox
= widget({ type = "textbox", name
= "text", align
= "flex" })
191 textbox
:buttons({ button({ }, 1, run
),
192 button({ }, 3, die
) })
193 textbox
.text
= string.format('<span font_desc="%s"><b>%s</b> %s</span>',
194 config
.font
, title
, text
)
195 if config
.hover_timeout
then textbox
.mouse_enter
= hover_destroy
end
200 iconbox
= widget({ type = "imagebox", name
= "icon", align
= "left" })
201 iconbox
:buttons({ button({ }, 1, run
),
202 button({ }, 3, die
) })
203 local img
= image(icon
)
205 img
= img
:crop_and_scale(0,0,img
.height
,img
.width
,icon_size
,icon_size
)
206 iconbox
.resize
= false
209 if config
.hover_timeout
then iconbox
.mouse_enter
= hover_destroy
end
212 -- create container wibox
213 notification
.box
= wibox({ name
= "not" .. notification
.idx
,
214 position
= "floating",
215 fg
= args
.fg
or config
.fg
,
216 bg
= args
.bg
or config
.bg
,
217 border_color
= config
.border_color
,
218 border_width
= config
.border_width
})
220 -- position the wibox
221 local lines
= 1; for i
in string.gmatch(title
..text
, "\n") do lines
= lines
+ 1 end
222 if iconbox
and iconbox
.image
.height
> lines
* config
.height
then
223 notification
.height
= iconbox
.image
.height
225 notification
.height
= lines
* config
.height
end
226 local offset
= get_offset(notification
.idx
, notification
.position
, notification
.height
)
227 notification
.box
:geometry({ width
= config
.width
,
228 height
= notification
.height
,
231 notification
.box
.ontop
= ontop
232 notification
.box
.screen
= screen
235 notification
.box
.widgets
= { iconbox
, textbox
}
237 -- insert the notification to the table
238 table.insert(notifications
[notification
.position
],notification
)
241 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80