summaryrefslogtreecommitdiff
path: root/lib/wibox/widget/systray.lua
blob: e83347ce0e99f89bb9cbc5ca71bb68a9f1e292f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
---------------------------------------------------------------------------
-- @author Uli Schlachter
-- @copyright 2010 Uli Schlachter
-- @classmod wibox.widget.systray
---------------------------------------------------------------------------

local wbase = require("wibox.widget.base")
local beautiful = require("beautiful")
local util = require("awful.util")
local capi = {
    awesome = awesome,
    screen = screen
}
local setmetatable = setmetatable
local error = error
local abs = math.abs

local systray = { mt = {} }

local instance = nil
local horizontal = true
local base_size = nil
local reverse = false
local display_on_screen = "primary"

--- The systray background color.
-- @beautiful beautiful.bg_systray
-- @param string The color (string like "#ff0000" only)

--- The systray icon spacing.
-- @beautiful beautiful.systray_icon_spacing
-- @tparam[opt=0] integer The icon spacing

local function should_display_on(s)
    if display_on_screen == "primary" then
        return s == capi.screen.primary
    end
    return s == display_on_screen
end

function systray:draw(context, cr, width, height)
    if not should_display_on(context.screen) then
        return
    end

    local x, y, _, _ = wbase.rect_to_device_geometry(cr, 0, 0, width, height)
    local num_entries = capi.awesome.systray()
    local bg = beautiful.bg_systray or beautiful.bg_normal or "#000000"
    local spacing = beautiful.systray_icon_spacing or 0

    if context and not context.wibox then
        error("The systray widget can only be placed inside a wibox.")
    end

    -- Figure out if the cairo context is rotated
    local dir_x, dir_y = cr:user_to_device_distance(1, 0)
    local is_rotated = abs(dir_x) < abs(dir_y)

    local in_dir, ortho, base
    if horizontal then
        in_dir, ortho = width, height
        is_rotated = not is_rotated
    else
        ortho, in_dir = width, height
    end
    if ortho * num_entries <= in_dir then
        base = ortho
    else
        base = in_dir / num_entries
    end
    capi.awesome.systray(context.wibox.drawin, math.ceil(x), math.ceil(y),
                         base, is_rotated, bg, reverse, spacing)
end

function systray:fit(context, width, height)
    if not should_display_on(context.screen) then
        return 0, 0
    end

    local num_entries = capi.awesome.systray()
    local base = base_size
    local spacing = beautiful.systray_icon_spacing or 0
    if num_entries == 0 then
        return 0, 0
    end
    if base == nil then
        if width < height then
            base = width
        else
            base = height
        end
    end
    base = base + spacing
    if horizontal then
        return base * num_entries - spacing, base
    end
    return base, base * num_entries - spacing
end

-- Check if the function was called like :foo() or .foo() and do the right thing
local function get_args(self, ...)
    if self == instance then
        return ...
    end
    return self, ...
end

--- Set the size of a single icon.
-- If this is set to nil, then the size is picked dynamically based on the
-- available space. Otherwise, any single icon has a size of `size`x`size`.
-- @tparam integer|nil size The base size
function systray:set_base_size(size)
    base_size = get_args(self, size)
    if instance then
        instance:emit_signal("widget::layout_changed")
    end
end

--- Decide between horizontal or vertical display.
-- @tparam boolean horiz Use horizontal mode?
function systray:set_horizontal(horiz)
    horizontal = get_args(self, horiz)
    if instance then
        instance:emit_signal("widget::layout_changed")
    end
end

--- Should the systray icons be displayed in reverse order?
-- @tparam boolean rev Display in reverse order
function systray:set_reverse(rev)
    reverse = get_args(self, rev)
    if instance then
        instance:emit_signal("widget::redraw_needed")
    end
end

--- Set the screen that the systray should be displayed on.
-- This can either be a screen, in which case the systray will be displayed on
-- exactly that screen, or the string `"primary"`, in which case it will be
-- visible on the primary screen. The default value is "primary".
-- @tparam screen|"primary" s The screen to display on.
function systray:set_screen(s)
    display_on_screen = get_args(self, s)
    if instance then
        instance:emit_signal("widget::layout_changed")
    end
end

--- Create the systray widget.
-- Note that this widget can only exist once.
-- @tparam boolean revers Show in the opposite direction
-- @treturn table The new `systray` widget
-- @function wibox.widget.systray

local function new(revers)
    local ret = wbase.make_widget()

    util.table.crush(ret, systray, true)

    if revers then
        ret:set_reverse(true)
    end

    capi.awesome.connect_signal("systray::update", function()
        ret:emit_signal("widget::layout_changed")
        ret:emit_signal("widget::redraw_needed")
    end)
    capi.screen.connect_signal("primary_changed", function()
        if display_on_screen == "primary" then
            ret:emit_signal("widget::layout_changed")
        end
    end)

    return ret
end

function systray.mt:__call(...)
    if not instance then
        instance = new(...)
    end
    return instance
end

return setmetatable(systray, systray.mt)

-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80