View difference between Paste ID: wUVeftNS and r40996h9
SHOW: | | - or go back to the newest paste.
1
-- CC Tweaked Forums: https://forums.computercraft.cc/index.php?topic=458.0
2
local count = {}
3
local increment = {index = 0, count = -1, max = -1}
4
local scrollPos = 0
5
local Vector2 = function(x, y)
6
    return {x = x, y = y}
7
end
8
local size = Vector2(term.getSize())
9
10
if fs.exists("simplecounter.txt") then -- Checks if counter data exists and loads it.
11
    local f = fs.open("simplecounter.txt","r")
12
    count = textutils.unserialize(f.readAll())
13
    f.close()
14
end
15
local saveCount = function() -- Saves counter data to a file.
16
    local f = fs.open("simplecounter.txt","w")
17
    f.write(textutils.serialize(count))
18
    f.close()
19
end
20
21
local drawLine = function(index) -- Draws all counter properties.
22
    term.setCursorPos(1, index-scrollPos)
23
    local str = "x R " .. string.char(24) .. string.char(25) .. " L -+" .. count[index].count .. "/M" -- Text for delete, rename, reposition, lock, count up/down, max, and max up/down buttons, and the count itself, and "/" (for count/max).
24
    local textColor = "fff" -- Text color for lock button and surrounding spaces.
25
    local backColor = "8f8f88f8f88" .. string.rep("0", string.len(count[index].count)) .. "f8" -- Background color for all buttons up to count up/down.
26
    if index == #count then -- Text color for the down reposition button.
27
        textColor = "7" .. textColor -- Gray if at the bottom of the list; can't move this counter down anymore.
28
    else
29
        textColor = "f" .. textColor -- Black if not at the bottom of the list.
30
    end
31
    if index == 1 then -- Text color for the up reposition button and the space before.
32
        textColor = "f7" .. textColor -- Button is gray if at the top of the list; can't move this counter up anymore.
33
    else
34
        textColor = "ff" .. textColor -- Black if not at the top of the list.
35
    end
36
    if count[index].locked then -- Text color for the remainder of the buttons as well as the count itself.
37
        textColor = "7f7" .. textColor .. "77" .. string.rep("f", string.len(count[index].count)) .. "07" -- Gray if this counter is locked.
38
    else
39
        textColor = "fff" .. textColor .. "ff" .. string.rep("f", string.len(count[index].count)) .. "0f" -- Black if this counter is not locked
40
    end
41
    if count[index].max then -- Draws the max number and percentage if the max number is enabled.
42
        local percent = math.floor(count[index].count/count[index].max*100000)/1000 -- The percentage of count/max.
43
        str = str .. "-+" .. count[index].max .. " " .. percent .. "%"
44
        if count[index].locked then
45
            textColor = textColor .. "77"
46
        else
47
            textColor = textColor .. "ff"
48
        end
49
        textColor = textColor .. string.rep("f", string.len(count[index].max)) .. "f" .. string.rep("4", string.len(percent)) .. "4"
50
        backColor = backColor .. "88" .. string.rep("0", string.len(count[index].max)) .. "f" .. string.rep("f", string.len(percent)) .. "f"
51
    end
52
    str = str .. " " .. count[index].name -- Text, text color, and background color for the counter name.
53
    textColor = textColor .. "f" .. string.rep("0", count[index].name:len())
54
    backColor = backColor .. "f" .. string.rep("f", count[index].name:len())
55
    str = str .. string.rep(" ", size.x-str:len()) -- Text, text color, background color for the empty space after the counter name.
56
    textColor = textColor .. string.rep("f", size.x-textColor:len())
57
    backColor = backColor .. string.rep("f", size.x-backColor:len())
58
    term.blit(str, textColor, backColor) -- Draws the above data to the screen.
59
end
60
local drawAll = function() -- Draws the whole screen.
61
    for a = 1, size.y do
62
        if a+scrollPos <= #count then
63
            drawLine(a+scrollPos)
64
        elseif a+scrollPos > #count then
65
            term.setCursorPos(1, a)
66
            term.setBackgroundColor(colors.black)
67
            term.clearLine()
68
            if a+scrollPos == #count+1 then
69
                term.blit("Add Exit", "ffffffff", "888f8888")
70
            end
71
        end
72
    end
73
end
74
75
local editName = function(name) -- Enters a name for a new or existing counter.
76
    term.setBackgroundColor(colors.black)
77
    term.clear()
78
    term.setCursorPos(1, 1)
79
    term.setTextColor(colors.yellow)
80
    term.write(name)
81
    term.setCursorPos(1, 2)
82
    term.setTextColor(colors.white)
83
    term.write("New Name: ")
84
    return read()
85
end
86
87
local addSub = function(index, key, dir) -- Adds or subtracts the count or max values by the increment amount.
88
    local anti = ""
89
    if key == "count" then
90
        anti = "max"
91
    else
92
        anti = "count"
93
    end
94
    if increment[key] < 1 then
95
        increment[key] = 1
96
    end
97
    increment[anti] = -1
98
    count[index][key] = count[index][key] + increment[key] * dir
99
    if string.len(math.abs(count[index][key])) < string.len(increment[key]) then -- Moves the selected digit if the number is smaller than the increment amount.
100
        increment[key] = 10 ^ (string.len(math.abs(count[index][key])) - 1)
101
    end
102
    saveCount()
103
    drawLine(index)
104
end
105
106
local selectDigit = function(index, key, digit) -- Selects the clicked digit and the increment buttons will increment that digit.
107
    local anti = ""
108
    if key == "count" then
109
        anti = "max"
110
    else
111
        anti = "count"
112
    end
113
    if digit > 1 and count[index][key] < 0 then
114
        digit = digit + 1
115
    end
116
    if digit == string.len(math.abs(count[index][key]))+1 then
117
        increment[key] = 0 -- If the space after the number is clicked, it sets the cursor to where keyboard input can add numbers to the end.
118
    else
119
        increment[key] = 10 ^ (string.len(math.abs(count[index][key])) - digit)
120
    end
121
    increment[anti] = -1
122
end
123
124
local changeIndex = function(dir) -- Processes changing which counter the cursor is on when pressing the up or down keys.
125
    repeat -- Scans the counters in the given direction until it either reaches the top or bottom of the list or finds a counter that's not locked.
126
        increment.index = increment.index + dir
127
    until increment.index == 0 or increment.index > #count or not count[increment.index].locked
128
    if increment.index == 0 or increment.index > #count then -- If an unlocked counter is not found the cursor is turned off.
129
        increment = {index = 0, count = -1, max = -1}
130
    elseif increment.index > 0 and increment.count < 0 and increment.max < 0 then
131
        increment.count = 0
132
    end
133
    if increment.count > -1 and string.len(increment.count) > string.len(math.abs(count[increment.index].count)) then -- This ensures the cursor is within the digits of the selected counter.
134
        increment.count = 10 ^ (string.len(math.abs(count[increment.index].count)) - 1)
135
    elseif increment.max > -1 then
136
        if count[increment.index].max then -- This ensures the cursor is within the digits of the max of the selected counter.
137
            if string.len(increment.max) > string.len(math.abs(count[increment.index].max)) then
138
                increment.max = 10 ^ (string.len(math.abs(count[increment.index].count)) - 1)
139
            end
140
        else -- If the max is disabled, the cursor will move to the count.
141
            increment.max = -1
142
            increment.count = 0
143
        end
144
    end
145
end
146
147
local deleteNum = function(index, key, space) -- This processes when the backspace or delete keys are pressed and the cursor is enabled.
148
    local digit = string.len(count[index][key]) - string.len(increment[key]) -- Locates where on the number the cursor is located.
149
    if increment[key] == 0 then
150
        digit = string.len(count[index][key])
151
    end
152
    if (space == 0 and digit > 0) or (space == 1 and digit < string.len(count[index][key])) then -- Check if backspace is pressed and the cursor is not at the very start of the number, or if delete is pressed and the cursor is not at the very end of the number.
153
        if string.len(math.abs(count[index][key])) == 1 then -- If the number is single digit when deleted, it is set to 0.
154
            count[index][key] = 0
155
        elseif digit == 0 then -- If delete (but not backspace) is pressed, deleted the first digit in the number.
156
            count[index][key] = tonumber(string.sub(count[index][key], digit+1+space, string.len(count[index][key])))
157
        elseif digit == string.len(count[index][key]) then -- If backspace (but not delete) is pressed, delete the last digit in the number.
158
            count[index][key] = tonumber(string.sub(count[index][key], 0, digit-1+space))
159
        else -- If delete is pressed, delete the current digit. If backspace is pressed, delete the previous digit.
160
            count[index][key] = tonumber(string.sub(count[index][key], 0, digit-1+space) .. string.sub(count[index][key], digit+1+space, string.len(count[index][key])))
161
        end
162
        if string.len(increment[key]) > string.len(math.abs(count[index][key])) then -- Ensures that the increment value is still within the boundaries of the selected number.
163
            increment[key] = 10 ^ (string.len(math.abs(count[index][key])) - 1)
164
        end
165
        if space == 1 then -- Decreases the increment value when delete is pressed.
166
            if increment[key] == 1 then
167
                increment[key] = 0
168
            else
169
                increment[key] = increment[key] / 10
170
            end
171
        end
172
        saveCount()
173
        drawLine(index)
174
    end
175
end
176
177
drawAll()
178
while true do
179
    if increment.index > 0 then -- Draws the cursor blink over whichever digit is being incremented or decremented if it is so.
180
        if increment.count > -1 then
181
            if increment.count > 0 then
182
                term.setCursorPos(12+string.len(count[increment.index].count)-string.len(increment.count), increment.index-scrollPos)
183
                term.setTextColor(colors.black)
184
            else
185
                term.setCursorPos(12+string.len(count[increment.index].count), increment.index-scrollPos)
186
                term.setTextColor(colors.white)
187
            end
188
            term.setCursorBlink(true)
189
        elseif count[increment.index].max and increment.max > -1 then
190
            if increment.max > 0 then
191
                term.setCursorPos(16+string.len(count[increment.index].count)+string.len(count[increment.index].max)-string.len(increment.max), increment.index-scrollPos)
192
                term.setTextColor(colors.black)
193
            else
194
                term.setCursorPos(16+string.len(count[increment.index].count)+string.len(count[increment.index].max), increment.index-scrollPos)
195
                term.setTextColor(colors.white)
196
            end
197
            term.setCursorBlink(true)
198
        end
199
    end
200
201
    local event = {os.pullEvent()}
202
    term.setCursorBlink(false)
203
    if event[1] == "mouse_click" then
204
        local clickPos = event[4] + scrollPos
205
        if clickPos == #count+1 then
206
            if event[3] >= 1 and event[3] <= 3 then -- Adds a counter to the list.
207
                table.insert(count, #count+1, {count=0, name=editName("New Counter"), locked=false})
208
                saveCount()
209
                drawAll()
210
            elseif event[3] >= 5 and event[3] <= 8 then -- Exits the program.
211
                term.setBackgroundColor(colors.black)
212
                term.setTextColor(colors.white)
213
                term.clear()
214
                term.setCursorPos(1, 1)
215
                error()
216
            end
217
            increment = {index = 0, count = -1, max = -1}
218
        elseif clickPos <= #count then -- Something on a counter is clicked.
219
            increment.index = clickPos
220
            if event[3] == 1 and not count[clickPos].locked then -- Deletes the counter if the counter is not locked.
221
                increment = {index = 0, count = -1, max = -1}
222
                table.remove(count, clickPos)
223
                term.setBackgroundColor(colors.black)
224
                term.clear()
225
                saveCount()
226
                drawAll()
227
            elseif event[3] == 3 and not count[clickPos].locked then -- Renames the counter if the counter is not locked.
228
                increment = {index = 0, count = -1, max = -1}
229
                count[clickPos].name = editName(count[clickPos].name)
230
                saveCount()
231
                drawAll()
232
            elseif event[3] == 5 and clickPos ~= 1 then -- Moves the counter up in the list if it is not at the top of the list. Locking does not affect this function.
233
                increment = {index = 0, count = -1, max = -1}
234
                table.insert(count, clickPos-1, count[clickPos])
235
                table.remove(count, clickPos+1)
236
                saveCount()
237
                drawLine(clickPos)
238
                drawLine(clickPos-1)
239
            elseif event[3] == 6 and clickPos ~= #count then -- Moves the counter down in the list if it is not at the bottom of the list. Locking does not affect this function.
240
                increment = {index = 0, count = -1, max = -1}
241
                table.insert(count, clickPos+2, count[clickPos])
242
                table.remove(count, clickPos)
243
                saveCount()
244
                drawLine(clickPos)
245
                drawLine(clickPos+1)
246
            elseif event[3] == 8 then -- Locks or unlocks the counter. Locking prevents accidentally deleting or changing the counter.
247
                increment = {index = 0, count = -1, max = -1}
248
                if count[clickPos].locked then
249
                    count[clickPos].locked = false
250
                else
251
                    count[clickPos].locked = true
252
                end
253
                saveCount()
254
                drawLine(clickPos)
255
            elseif event[3] == 10 and not count[clickPos].locked then -- Decrements the count if the counter is not locked.
256
                addSub(clickPos, "count", -1)
257
            elseif event[3] == 11 and not count[clickPos].locked then -- Increments the count if the counter is not locked.
258
                addSub(clickPos, "count", 1)
259
            elseif event[3] >= 12 and event[3] <= 12+string.len(count[clickPos].count) and not count[clickPos].locked then -- If the count number is clicked, it selects the clicked digit and the increment buttons will increment that digit. Will do nothing if the counter is locked.
260
                selectDigit(clickPos, "count", event[3] - 11)
261
            elseif event[3] == 13+string.len(count[clickPos].count) and not count[clickPos].locked then -- Toggles the max value if the counter is not locked.
262
                increment = {index = 0, count = -1, max = -1}
263
                if count[clickPos].max then
264
                    count[clickPos].max = nil
265
                else
266
                    count[clickPos].max = 0
267
                end
268
                saveCount()
269
                drawLine(clickPos)
270
            elseif event[3] == 14+string.len(count[clickPos].count) and count[clickPos].max and not count[clickPos].locked then -- Decrements the max value if it is enabled and the counter is not locked.
271
                addSub(clickPos, "max", -1)
272
            elseif event[3] == 15+string.len(count[clickPos].count) and count[clickPos].max and not count[clickPos].locked then -- Increments the max value if it is enabled and the counter is not locked.
273
                addSub(clickPos, "max", 1)
274
            elseif count[clickPos].max and event[3] >= 16+string.len(count[clickPos].count) and event[3] <= 16+string.len(count[clickPos].count)+string.len(count[clickPos].max) and not count[clickPos].locked then -- If the max number is clicked, it selects the clicked digit and the increment buttons will increment that digit. Will do nothing if the counter is locked.
275
                selectDigit(clickPos, "max", event[3] - string.len(count[clickPos].count) - 15)
276
            else
277
                increment = {index = 0, count = -1, max = -1}
278
            end
279
        else
280
            increment = {index = 0, count = -1, max = -1}
281
        end
282
    elseif event[1] == "mouse_scroll" then -- Scrolls the list with the mouse wheel.
283
        if (event[2] == -1 and scrollPos > 0) or (event[2] == 1 and scrollPos+size.y-1 <= #count) then
284
            scrollPos = scrollPos + event[2]
285
            drawAll()
286
        end
287
    elseif event[1] == "char" and increment.index > 0 then
288
        if event[2] == "-" then -- Toggles the selected number between positive and negative.
289
            if increment.count > -1 then
290
                count[increment.index].count = count[increment.index].count * -1
291
                saveCount()
292
                drawLine(increment.index)
293
            elseif increment.max > -1 then
294
                count[increment.index].max = count[increment.index].max * -1
295
                saveCount()
296
                drawLine(increment.index)
297
            end
298
        elseif tonumber(event[2]) then -- Inserts the input number into the selected number.
299
            if increment.count > -1 then
300
                local digit = string.len(count[increment.index].count) - string.len(increment.count)
301
                if increment.count == 0 then
302
                    digit = string.len(count[increment.index].count)
303
                end
304
                count[increment.index].count = tonumber(string.sub(count[increment.index].count, 0, digit) .. event[2] .. string.sub(count[increment.index].count, digit+1, string.len(count[increment.index].count)+1))
305
                saveCount()
306
                drawLine(increment.index)
307
            elseif increment.max > -1 then
308
                local digit = string.len(count[increment.index].max) - string.len(increment.max)
309
                if increment.max == 0 then
310
                    digit = string.len(count[increment.index].max)
311
                end
312
                count[increment.index].max = tonumber(string.sub(count[increment.index].max, 0, digit) .. event[2] .. string.sub(count[increment.index].max, digit+1, string.len(count[increment.index].max)+1))
313
                saveCount()
314
                drawLine(increment.index)
315
            end
316
        end
317
    elseif event[1] == "key" then
318
        if event[2] == keys.left and increment.index > 0 then -- Move the cursor left along the numbers in the current counter.
319
            if increment.max == 0 then
320
                increment.max = 1
321
            elseif increment.max > -1 and string.len(increment.max) < string.len(math.abs(count[increment.index].max)) then
322
                increment.max = increment.max * 10
323
            elseif increment.max > -1 and string.len(increment.max) == string.len(math.abs(count[increment.index].max)) then
324
                increment.max = -1
325
                increment.count = 0
326
            elseif increment.count == 0 then
327
                increment.count = 1
328
            elseif string.len(increment.count) < string.len(math.abs(count[increment.index].count)) then
329
                increment.count = increment.count * 10
330
            end
331
        elseif event[2] == keys.right and increment.index > 0 then -- Move the cursor right along the numbers in the current counter. Will not move to the max if max is disabled.
332
            if increment.count > 1 then
333
                increment.count = increment.count / 10
334
            elseif increment.count == 1 then
335
                increment.count = 0
336
            elseif increment.count == 0 and count[increment.index].max then
337
                increment.count = -1
338
                increment.max = 10 ^ (string.len(math.abs(count[increment.index].max)) - 1)
339
            elseif increment.max > 1 then
340
                increment.max = increment.max / 10
341
            elseif increment.max == 1 then
342
                increment.max = 0
343
            end
344
        elseif event[2] == keys.up and increment.index > 0 then -- Scans for the previous unlocked counter or deselects the counter.
345
            changeIndex(-1)
346
        elseif event[2] == keys.down and increment.index <= #count then -- Scans for the next unlocked counter or deselects the counter.
347
            changeIndex(1)
348
        elseif event[2] == keys.backspace and increment.index > 0 then -- Deletes the previous digit in the selected number.
349
            if increment.count > -1 then
350
                deleteNum(increment.index, "count", 0)
351
            elseif increment.max > -1 then
352
                deleteNum(increment.index, "max", 0)
353
            end
354
        elseif event[2] == keys.delete and increment.index > 0 then -- Deletes the current digit in the selected number.
355
            if increment.count > -1 then
356
                deleteNum(increment.index, "count", 1)
357
            elseif increment.max > -1 then
358
                deleteNum(increment.index, "max", 1)
359
            end
360
        elseif event[2] == keys.home and increment.index > 0 then -- Move the cursor to the start of the selected counter.
361
            if increment.max > -1 and (increment.max == 0 or string.len(increment.max) < string.len(math.abs(count[increment.index].max))) then -- If max is selected, moves the cursor to the start of the max.
362
                increment.max = 10 ^ (string.len(math.abs(count[increment.index].max)) - 1)
363
            else -- If the cursor is at the start of the max or is in counter, moves the cursor to the start of the counter.
364
                increment.max = -1
365
                increment.count = 10 ^ (string.len(math.abs(count[increment.index].count)) - 1)
366
            end
367
        elseif event[2] == keys["end"] and increment.index > 0 then -- Moves the cursor to the end of the selected counter.
368
            if increment.count > -1 and increment.count > 0 then -- If counter is selected, moves the cursor to the end of the counter.
369
                increment.count = 0
370
            else -- If at the end of the counter and max exists, or if max is selected, moves the cursor to the end of the max.
371
                if count[increment.index].max then
372
                    increment.count = -1
373
                    increment.max = 0
374
                end
375
            end
376
        end
377
    elseif event[1] == "term_resize" then
378
        size = Vector2(term.getSize())
379
    end
380
end
381
382
-- 2022/11/08
383
--  Fixed a bug not displaying counters correctly when scrolling.