-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcoolwidget.lua
237 lines (203 loc) · 7.7 KB
/
coolwidget.lua
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
---@diagnostic disable: empty-block, unused-function
local wibox = require "wibox"
local base = require "wibox.widget.base"
-- must be something they have a setter for
-- as in set_propname must exist
local PROPERTIES = {
margin = { "left", "right", "top", "bottom", "margins" },
constraint = { "width", "height", "strategy" },
background = { "bg", "shape" },
place = { "valign", "halign" }
}
--helper table function
local function index(table, value)
for k,v in pairs(table) do if v == value then return k end end
return false
end
-- literally just contains a widget
-- that's literally it
local function simple_container()
local w = wibox.widget.base.make_widget()
function w:fit(context, width, height)
if not self._private.widget then return 0, 0 end
return base.fit_widget(self, context, self._private.widget, width, height)
end
function w:layout(_, width, height)
if not self._private.widget then return end
return { base.place_widget_at(self._private.widget, 0, 0, width, height) }
end
function w:set_children(widgets) self._private.widget = widgets[1] end
function w:get_children() return { self._private.widget } end
return w
end
-- literally just transparent
-- emulates its child exactly
local function passthrough_container()
local w = wibox.widget.base.make_widget(nil, nil, {enable_properties=true})
function w:set_children(widgets) w._private.widget = widgets[1] end
function w:get_children() return { w._private.widget } end
function w:fit(...) return w._private.widget:fit(...) end
function w:layout(...) return w._private.widget:layout(...) end
function w:before_draw_children(...) if w._private.widget.before_draw_children then w._private.widget:before_draw_children(...) end end
function w:after_draw_children(...) if w._private.widget.after_draw_children then w._private.widget:after_draw_children(...) end end
return w
end
-- align layout but better becuase it also has margins and uses expand
local function alignplus(orientation)
--private properties
--not put in _private so as to not mess with weird widget.base stuff
local expand = "default"
local spacing = 0
local widgets = {}
--direction for padding, saved as to not have to do multiple times
local margin_direction = orientation == "horizontal" and "left" or "bottom"
--create the widget
local w = wibox.layout.align[orientation]()
--these margins act as containers for the first and second widgets and are
--used for emulating spacing
local margin1, margin2 = wibox.container.margin(), wibox.container.margin()
w:set_children { margin1, margin2 }
--save the existing metatable for modification
local mt = getmetatable(w)
setmetatable(w, {})
--getters and setters for properties
function w:set_expand(value)
expand = value
w:set_children()
end
function w:set_spacing(value)
spacing = value
if expand == "default" then
margin1[margin_direction] = spacing
margin2[margin_direction] = spacing
elseif expand == "expfirst" then
margin1[margin_direction] = 0
margin2[margin_direction] = spacing
elseif expand == "explast" or expand == "neither" then
margin1[margin_direction] = spacing
margin2[margin_direction] = 0
end
end
function w:get_expand() return expand end
function w:get_spacing() return spacing end
--save old __index and __newindex as fallbacks
local ___index = mt.__index
local ___newindex = mt.___newindex
--do accessors
function mt:__index(key)
if key == "spacing" then return w:get_spacing()
elseif key == "expand" then return w:get_expand()
else return ___index(self, key) end
end
function mt:__newindex(key, value)
if key == "spacing" then w:set_spacing(value)
elseif key == "expand" then w:set_expand(value)
else ___newindex(self, key, value) end
end
--override set_children and get_children to account for margins
function w:set_children(value)
widgets = value or {}
if expand == "default" then
margin1:set_children { widgets[1] }
margin2:set_children { widgets[2] }
w:set_third(widgets[3])
elseif expand == "expfirst" then
margin1:set_children { nil }
margin2:set_children { widgets[1] }
w:set_third(widgets[2])
elseif expand == "explast" then
margin1:set_children { widgets[1] }
margin2:set_children { widgets[2] }
w:set_third(nil)
elseif expand == "neither" then
margin1:set_children { widgets[1] }
margin2:set_children { nil }
w:set_third(widgets[2])
end
w:set_spacing(spacing)
if not margin1:get_children()[1] then margin1[margin_direction] = 0 end
if not margin2:get_children()[1] then margin2[margin_direction] = 0 end
end
function w:get_children() return widgets end
--rewrite metatble
setmetatable(w, mt)
return w
end
--- Cool widget
--
-- args is a list of strings, all but the last 1-2 being container names
-- and the last 1-2 determining layout type and orientation. It maps
-- properties set to its designated container with the PROPERTIES constant
local function create(args)
---@diagnostic disable-next-line: unused-local
local debug; if args[#args] == "debug" then debug = true; table.remove(args, #args) end
--parts is where we save all the containers
local parts = {}
--determine the main type of the widget
--the only "special" main types are "align" and "container"
local main
if args[#args] == "container" then main = simple_container()
elseif args[#args-1] == "align" then main = alignplus(args[#args])
else main = wibox.layout[args[#args-1]][args[#args]]() end
--remove tail
if args[#args] ~= "container" then table.remove(args, #args) end
table.remove(args, #args)
--creates this base widget
local w = passthrough_container()
--populates w and parts
--does so by iterating through args and adding each widget
--to the widget before it, except for the first one which
--gets added to w
for i=1,#args do
local c = wibox.container[args[i]]()
parts[args[i]] = c
if i == 1 then w:set_children { c }
else parts[args[i-1]]:set_children { c } end
end
--finally, add in main to the last widget
if #args > 0 then parts[args[#args]]:set_children { main }
else w:set_children { main } end
--do all the metatable stuff
local mt = getmetatable(w)
setmetatable(w, {})
local ___index = mt.__index
local ___newindex = mt.__newindex
function mt:__index(key)
--autogenerate setters
if key:match("set_") then
for k, v in pairs(PROPERTIES) do if index(v, key:sub(5)) then return function(_, value) rawget(parts[k], key)(parts[k], value) end end end
return nil --if it's not in there just return nothing
end
--if it's expand or spacing we forward it to main
if key == "expand" or key == "spacing" then return rawget(main, "get_"..key)(main) end
--check if it's in properties anywhere
for k, v in pairs(PROPERTIES) do if index(v, key) then return rawget(parts[k], "get_"..key)(parts[k]) end end
--otherwise pass to widget.base
return ___index(self, key)
end
function mt:__newindex(key, value)
--if it's expand or spacing we forward it to main
if key == "expand" or key == "spacing" then return rawget(main, "set_"..key)(main, value) end
--check for property and setter
for k, v in pairs(PROPERTIES) do if index(v, key:gsub("set_", "")) then return rawget(parts[k], "set_"..key)(parts[k], value) end end
--otherwise pass to widget.base
return ___newindex(self, key, value)
end
--do the last bit of api stuff
function w:set_children(widgets) main:set_children(widgets) end
function w:get_children() return main:get_children() end
setmetatable(w, mt)
return w
end
local function get_containers_recursive(args)
local mt = {}
function mt:__index(key)
local new = {}
for k,v in pairs(args) do new[k]=v end
table.insert(new, key)
return get_containers_recursive(new)
end
function mt:__call() return create(args) end
return setmetatable({is_widget=false}, mt)
end
return get_containers_recursive {}