-
Notifications
You must be signed in to change notification settings - Fork 0
/
area.lua
290 lines (236 loc) · 6.96 KB
/
area.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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
-- ▄▀█ █▀█ █▀▀ ▄▀█
-- █▀█ █▀▄ ██▄ █▀█
-- Basic unit for the nav hierarchy. An area's items can be
-- nav elements or other areas.
local gobject = require("gears.object")
local LEFT = -1
local NONE = 0
local RIGHT = 1
local debug = true
local function dbprint(...)
if debug then print(...) end
end
local area = {}
area.__index = area
setmetatable(area, {
__call = function(class, ...)
return class:new(...)
end
})
function area:new(args)
args = args or {}
self = setmetatable(gobject{}, area)
self.widget = args.widget
self.name = args.name or "unnamed_area"
self.type = "area"
-- Index within parent (areas can be items within another area)
self.index = 1
-- TODO: Don't remember what this does lol
self.autofocus = args.autofocus or false
self.is_wrapper = args.is_wrapper or false
-- Grid stuff
self.is_grid = args.is_grid or false
self.num_rows = args.num_rows
self.num_cols = args.num_cols
-- Doubly linked list stuff
self.parent = nil
self.next = self
self.prev = self
self.items = {}
if args.items then
for i = 1, #args.items do
self:append(args.items[i])
end
end
-- Keybound functions
self.keys = args.keys or {}
self.override_keys = args.override_keys or {}
-- Reference to navigator
self.nav = args.nav
if args.parent_area then
args.parent_area:append(self)
end
return self
end
function area:__concat()
return self.name
end
function area:__eq(b)
return self.name == b.name
end
-- █ █▄░█ █▀ █▀▀ █▀█ ▀█▀ ░░▄▀ █▀▄ █▀▀ █░░ █▀▀ ▀█▀ █▀▀
-- █ █░▀█ ▄█ ██▄ █▀▄ ░█░ ▄▀░░ █▄▀ ██▄ █▄▄ ██▄ ░█░ ██▄
function area:insert_at(index, item)
print('INSERT AT UNIMPLEMENTED')
end
function area:prepend(item) end
--- @method append
-- @brief Append new item to end of list
function area:append(item)
item.index = #self.items + 1
item.parent = self
if item.type == "area" then
item.nav = self.nav
end
local first = self.items[1]
local last = self.items[#self.items]
if #self.items > 0 then
last.next = item
first.prev = item
item.prev = last
item.next = first
else
self.active_element = item
item.prev = item
item.next = item
end
self.items[#self.items+1] = item
end
--- @method remove
-- @brief Remove an element from this area by index.
-- TODO: Needs serious testing
function area:remove(index)
print('Area: Removing by index')
-- Get index of currently active element
local active_idx = 1
for i = 1, #self.items do
if self.items[i] == self.active_element then
active_idx = i
end
end
-- TODO: What if it's the last remaining element?
if index == active_idx then
self:iter()
else
-- Fix doubly linked list references
local target_prev = self.items[index].prev
local target_next = self.items[index].next
target_prev.next = target_next
target_next.prev = target_prev
table.remove(self.items, index)
self:update_indices()
return
end
end
--- @method remove_area
-- @brief Remove a subarea from this area.
-- @param target (string) The name of the area to remove.
function area:remove_area(target)
for i = 1, #self.items do
if self.items[i].type == "area" then
if self.items[i].name == target then
-- Fix doubly linked list references
local target_prev = self.items[i].prev
local target_next = self.items[i].next
target_prev.next = target_next
target_next.prev = target_prev
table.remove(self.items, i)
self:update_indices()
self.nav:emit_signal("area::removed", target)
return
else
self.items[i]:remove_area(target)
end
end
end
end
--- @method clear
-- @brief Remove all items from this area.
function area:clear()
self.items = {}
self.active_element = nil
if self.nav then self.nav:emit_signal("area::cleared") end
end
-- █▄░█ ▄▀█ █░█
-- █░▀█ █▀█ ▀▄▀
--- @method iter
-- @param dir
function area:iter(dir)
if dir == LEFT then
self.active_element = self.active_element.prev
elseif dir == RIGHT then
self.active_element = self.active_element.next
end
return self.active_element
end
function area:set_active_element(element)
self.active_element = element
end
function area:set_active_element_by_index(index)
self.active_element = self.items[index]
end
function area:force_active()
if self.nav then self.nav:set_focused_area(self) end
end
-- █▀▄▀█ █ █▀ █▀▀
-- █░▀░█ █ ▄█ █▄▄
--- @method dump
-- @brief Print area contents for debugging
function area:dump(space)
space = space or ""
local actelm = self.active_element
dbprint(space.."'"..self.name.."["..(actelm and actelm.index or 0).."] "..
'(P:'..(self.prev.name or "-")..
', N:'..(self.next.name or "-")..')'.. ": "..#self.items.." items")
space = space .. " "
for i = 1, #self.items do
if self.items[i].type == "area" then
self.items[i]:dump(space .. " ")
else
dbprint(space..'['..i..'] P:'..self.items[i].prev.index..' N:'..self.items[i].next.index)
end
end
end
--- @method contains_area
-- Check if this area contains a given area.
-- For this to work properly, the area's names must be set.
-- @param target (string) The name of the area to look for
function area:contains_area(target)
if self.name == target then return true end
for i = 1, #self.items do
if self.items[i].type == "area" then
if self.items[i]:contains_area(target) then return true end
end
end
return false
end
--- @method verify_nav_references
-- @brief Make sure every sub-area contained within this one has
-- a reference to the main navigator. This is so an area can send signals
-- to the navigator. This function is only called on the nav_root area.
function area:verify_nav_references()
for i = 1, #self.items do
if self.items[i].type == "area" then
self.items[i].nav = self.nav
self.items[i]:verify_nav_references()
end
end
end
--- @method update_indices
-- @brief Update item indices. Usually called after something has been
-- removed.
function area:update_indices()
for i = 1, #self.items do
self.items[i].index = i
end
end
-- ▄▀█ █▀▀ ▀█▀ █ █▀█ █▄░█ █▀
-- █▀█ █▄▄ ░█░ █ █▄█ █░▀█ ▄█
function area:select_on()
if self.active_element and self.active_element.emit_signal then
self.active_element:emit_signal("mouse::enter")
end
if self.widget and self.widget.emit_signal then
self.widget:emit_signal("mouse::enter")
end
end
function area:select_off()
if self.active_element and self.active_element.emit_signal then
self.active_element:emit_signal("mouse::leave")
end
if self.widget and self.widget.emit_signal then
self.widget:emit_signal("mouse::leave")
end
end
function area:release() end
return area