View difference between Paste ID: JXVa2vNX and iKzRve2g
SHOW: | | - or go back to the newest paste.
1
local gpu=require("component").gpu
2
local isPrimary=require("component").isPrimary
3
local event=require("event")
4
local len=require("unicode").len
5
local sub=require("unicode").sub
6
local uchar=require("unicode").char
7
local term=require("term")
8
local pushSignal=require("computer").pushSignal
9
local kbd  = require("component").keyboard
10
local wrap = require("text").wrap
11
local padRight=require("text").padRight
12
local isControl= require("keyboard").isControl
13
14
local forms={}
15
local mouseEv={touch=true, scroll=true, drag=true, drop=true}
16
local activeForm
17
18
local TComponent={left=1,top=1, color=0, fontColor=0xffffff, border=0, visible=true, tag=0, type=function() return "unknown" end}
19
TComponent.__index=TComponent
20
21
function TComponent:paint() end
22
23
function TComponent:isVisible()
24
  if not self.visible then return false end
25
  if self.parent then return self.parent:isVisible()
26
  else return self==activeForm
27
  end
28
end
29
30
function TComponent:draw()
31
  if self.parent then self.X=self.parent.X+self.left-1 self.Y=self.parent.Y+self.top-1
32
  else self.X=self.left self.Y=self.top end
33
  gpu.setBackground(self.color)
34
  gpu.setForeground(self.fontColor)
35
  local brd=nil
36
  if self.border==1 then brd={"┌","─","┐","└","│","┘"}
37
  elseif self.border==2 then brd={"╔","═","╗","╚","║","╝"}
38
  end
39
  if brd then
40
    gpu.set(self.X,self.Y, brd[1]..string.rep(brd[2],self.W-2)..brd[3])
41
    for i=self.Y+1,self.Y+self.H-2 do
42
      gpu.set(self.X,i, brd[5]..string.rep(" ",self.W-2)..brd[5])
43
    end
44
    gpu.set(self.X,self.Y+self.H-1, brd[4]..string.rep(brd[2],self.W-2)..brd[6])
45
  else gpu.fill(self.X,self.Y,self.W,self.H," ") end
46
  self:paint()
47
  if self.elements then
48
    for i=1,#self.elements do
49
      if self.elements[i].visible then self.elements[i]:draw() end
50
	end
51
  end
52
  if self.onDraw then self.onDraw() end -- Вот эта строчка
53
end
54
55
function TComponent:redraw() if self:isVisible() then self:draw() end end
56
57
function TComponent:makeChild(el)
58
  if not self.elements then self.elements={} end
59
  el.parent=self
60
  table.insert(self.elements,el)
61
end
62
63
function TComponent:mouseEv(ev,x,y,btn,user)
64
  if self.elements then
65
    for i=#self.elements,1,-1 do
66
      local e=self.elements[i]
67
	  if e.visible and e.X and x>=e.X and x<e.X+e.W and y>=e.Y and y<e.Y+e.H then
68
        e:mouseEv(ev,x,y,btn,user)
69
        return
70
      end
71
    end
72
  end  
73
  if self[ev] then self[ev](self, x-self.X+1,y-self.Y+1,btn,user) end
74
end
75
76
function TComponent:hide()
77
  if self.parent then
78
    self.visible=false
79
    self.parent:draw()
80
  else 
81
    gpu.setBackground(0)
82
    gpu.fill(self.X,self.Y,self.W,self.H," ")
83
  end
84
end
85
86
function TComponent:show()
87
  if self.parent then
88
    self.visible=true
89
	self.parent:draw()
90
  else 
91
    self:draw()
92
  end
93
end
94
95
function TComponent:destruct()
96
  if self.parent then
97
    for i=1,#self.parent.elements do
98
      if self.parent.elements[i]==self then table.remove(self.parent.elements,i) break end
99
	end
100
  end
101
end
102
103
function forms.activeForm() return activeForm end
104
105
----------Визуальные компоненты---------
106
------------------Form------------------
107
local TForm=setmetatable({type=function() return "Form" end},TComponent)
108
TForm.__index=TForm
109
110
function TForm:isActive() return self==activeForm end
111
112
function TForm:setActive()
113
  if activeForm~=self then
114
    activeForm=self
115
    self:show()
116
  end  
117
end
118
119
function forms.addForm()
120
  local obj={}
121
  TForm.W, TForm.H=gpu.getResolution()
122
  return setmetatable(obj,TForm)
123
end
124
125
------------------Button----------------
126
local TButton=setmetatable({W=10, H=1, color=0x606060, type=function() return "Button" end},TComponent)
127
TButton.__index=TButton
128
129
function TButton:touch(x, y, btn, user)
130
  if self.onClick and btn==0 then self:onClick(user) end
131
end
132
133
function TButton:paint()
134
  gpu.set(self.X+(self.W-len(self.caption))/2,self.Y+(self.H-1)/2, self.caption)
135
end
136
137
function TComponent:addButton(left, top, caption, onClick)
138
  local obj={left=left, top=top, caption=caption or "Button", onClick=onClick}
139
  self:makeChild(obj)
140
  return setmetatable(obj,TButton)
141
end
142
143
------------------Label-----------------
144
local TLabel=setmetatable({H=1, centered=false, alignRight=false, autoSize=true, type=function() return "Label" end} ,TComponent)
145
TLabel.__index=TLabel
146
147
function TLabel:paint()
148
 local line
149
 local value=tostring(self.caption)
150
 if self.autoSize then
151
   self.W,self.H=0,0
152
   for line in value:gmatch("([^\n]+)") do
153
     self.H=self.H+1
154
	 if len(line)>self.W then self.W=len(line) end
155
   end
156
   if self.W<1 then self.W=1 end
157
   if self.H<1 then self.H=1 end
158
 end
159
 for i=0,self.H-1 do
160
  if not value then break end
161
  line, value = wrap(value, self.W, self.W)
162
  if self.centered then gpu.set(self.X+(self.W-len(line))/2,self.Y+i, line)
163
  else 
164
    if self.alignRight then gpu.set(self.X+self.W-len(line),self.Y+i, line)
165
    else gpu.set(self.X,self.Y+i, line) end
166
  end
167
 end
168
end
169
170
function TComponent:addLabel(left, top, caption)
171
  local obj={left=left, top=top, caption=caption or "Label"}
172
  obj.W=len(obj.caption)
173
  self:makeChild(obj)
174
  return setmetatable(obj,TLabel)
175
end
176
177
------------------Edit------------------
178
local TEdit=setmetatable({W=20, H=3, text="", border=1, type=function() return "Edit" end},TComponent)
179
TEdit.__index=TEdit
180
181
function TEdit:paint()
182
  if type(self.text)=="table" then
183
    for i=1,self.H-2 do gpu.set(self.X+1,self.Y+i,sub(self.text[i] or "",1,self.W-2)) end
184
  else gpu.set(self.X+1,self.Y+1, sub(self.text,1,self.W-2))
185
  end
186
end
187
188
local function editText(text,left,top,W,H)
189
local running=true
190
local scrollX, scrollY = 0, 0
191
local posX, posY =1, 1
192
local writeText
193
194
local function setCursor(nx,ny)
195
  posX=nx or posX
196
  posY=ny or posY
197
  if #text<1 then text[1]="" end
198
  if posY>#text then posY=#text end
199
  if posY<1 then posY=1 end
200
  if posX>len(text[posY])+1 then posX=len(text[posY])+1 end
201
  if posX<1 then posX=1 end
202
  local redraw=false
203
  if posY<=scrollY then scrollY=posY-1 redraw=true end
204
  if posY>scrollY+H then scrollY=posY-H redraw=true end
205
  if posX<=scrollX then scrollX=posX-1 redraw=true end
206
  if posX>scrollX+W then scrollX=posX-W redraw=true end
207
  if redraw then writeText()
208
  else term.setCursor(left+posX-scrollX-1, top+posY-scrollY-1) end
209
end
210
  
211
local function writeLine(n)
212
  gpu.set(left,top+n-scrollY-1,padRight(sub(text[n] or "",scrollX+1,scrollX+W),W))
213
end
214
  
215
function writeText()
216
  for i=1,H do writeLine(i+scrollY) end
217
  setCursor()
218
end
219
220
local function insert(value)
221
  if not value or len(value) < 1 then return end
222
  text[posY]=sub(text[posY],1,posX-1)..value..sub(text[posY],posX)
223
  writeLine(posY)
224
  setCursor(posX+len(value))
225
end
226
  
227
local keys={}
228
keys[203]=function()    -- Left
229
  if posX>1 then setCursor(posX-1)
230
  else if posY>1 then posY=posY-1 setCursor(len(text[posY])+1) end
231
  end
232
end
233
keys[205]=function()   -- Right
234
  if posX<=len(text[posY]) then setCursor(posX+1)
235
  else if posY<#text then setCursor(1,posY+1) end
236
  end
237
end
238
keys[199]=function() setCursor(1) end   -- Home
239
keys[207]=function() setCursor(len(text[posY])+1) end   -- End
240
keys[211]=function()    -- Del
241
  if posX<=len(text[posY]) then
242
    text[posY]=sub(text[posY],1,posX-1)..sub(text[posY],posX+1)
243
    writeLine(posY)
244
  else
245
    if posY<#text then
246
	  text[posY]=text[posY]..text[posY+1]
247
	  table.remove(text,posY+1)
248
	  writeText()
249
    end
250
  end
251
end
252
keys[14] =function()    -- BackSp
253
  if posX>1 then
254
    text[posY]=sub(text[posY],1,posX-2)..sub(text[posY],posX)
255
    writeLine(posY)
256
    setCursor(posX-1)
257
  else
258
    if posY>1 then
259
      posX,posY,text[posY-1]=len(text[posY-1])+1,posY-1,text[posY-1]..text[posY]
260
	  table.remove(text,posY+1)
261
	  writeText()
262
    end
263
  end
264
end
265
keys[15] =function() insert("  ") end   -- Tab
266
  
267
local function onKeyDown(char, code)
268
  if keys[code] then keys[code]()
269
  else if not isControl(char) then insert(uchar(char)) end
270
  end
271
end
272
273
local function onClipboard(value)
274
end
275
276
local function onClick(x,y)
277
  if x>=left and x<left+W and y>=top and y<top+H then
278
    setCursor(x+scrollX-left+1,y+scrollY-top+1)
279
  else running=false
280
  end
281
end
282
283
local function onScroll(direction)
284
end
285
  
286
if type(text)=="table" then
287
  keys[68] =function() running=false end   -- F10
288
  keys[200]=function() setCursor(posX,posY-1) end   -- Up
289
  keys[208]=function() setCursor(posX,posY+1) end   -- Down
290
  keys[28] =function()   -- Enter
291
    local n=len(text[posY]:match("^%s*"))
292
    table.insert(text,posY+1,string.rep(" ",n)..sub(text[posY],posX))
293
	text[posY]=sub(text[posY],1,posX-1)
294
	posX,posY=n+1,posY+1
295
	writeText()
296
  end
297
else
298
  posX=len(text)+1
299
  text={tostring(text)}
300
  keys[28] =function() running=false end   -- Enter
301
end
302
term.setCursorBlink(true)
303
writeText()
304
local event, address, arg1, arg2, arg3
305
while running do
306
  event, address, arg1, arg2, arg3 = term.pull()
307
  if type(address) == "string" and isPrimary(address) then
308
    term.setCursorBlink(false)
309
    if event == "key_down" then onKeyDown(arg1, arg2)
310
    elseif event == "clipboard" then onClipboard(arg1)
311
    elseif event == "touch" or event == "drag" then onClick(arg1, arg2)
312
    elseif event == "scroll" then onScroll(arg3)
313
    end
314
    term.setCursorBlink(true)
315
  end
316
end
317
if event=="touch" then pushSignal( event, address, arg1, arg2, arg3 ) end
318
term.setCursorBlink(false)
319
return text[1]
320
end
321
322
function TEdit:touch(x, y, btn, user)
323
  if btn==0 then
324
    gpu.setBackground(self.color)
325
    gpu.setForeground(self.fontColor)
326
	if type(self.text)=="table" then editText(self.text,self.X+1,self.Y+1,self.W-2,self.H-2)
327
	else self.text=editText(self.text,self.X+1,self.Y+1,self.W-2,1)	end
328
	self:draw()
329
    if self.onEnter then self:onEnter(user) end
330
  end
331
end
332
333
function TComponent:addEdit(left, top, onEnter)
334
  local obj={left=left, top=top, onEnter=onEnter}
335
  self:makeChild(obj)
336
  return setmetatable(obj,TEdit)
337
end
338
339
------------------Frame-----------------
340
local TFrame=setmetatable({W=20, H=10, border=1, type=function() return "Frame" end},TComponent)
341
TFrame.__index=TFrame
342
343
function TComponent:addFrame(left, top, border)
344
  local obj={left=left, top=top, border=border}
345
  self:makeChild(obj)
346
  return setmetatable(obj,TFrame)
347
end
348
349
------------------List------------------
350
local TList=setmetatable({W=20, H=10, border=2, selColor=0x0000ff, sfColor=0xffff00, shift=0, index=0,
351
  type=function() return "List" end},TComponent)
352
TList.__index=TList
353
354
function TList:paint()
355
  local b= self.border==0 and 0 or 1
356
  for i=1,self.H-2*b do
357
	if i+self.shift==self.index then gpu.setForeground(self.sfColor) gpu.setBackground(self.selColor) end
358
    gpu.set(self.X+b,self.Y+i+b-1, padRight(sub(self.lines[i+self.shift] or "",1,self.W-2*b),self.W-2*b))
359
	if i+self.shift==self.index then gpu.setForeground(self.fontColor) gpu.setBackground(self.color) end
360
  end
361
end
362
363
function TList:clear()
364
  self.shift=0 self.index=0 self.lines={} self.items={}
365
  self:redraw()
366
end
367
368
function TList:insert(pos,line,item)
369
  if type(pos)~="number" then pos,line,item=#self.lines+1,pos,line end
370
  table.insert(self.lines,pos,line)
371
  table.insert(self.items,pos,item or false)
372
  if self.index<1 then self.index=1 end
373
  if pos<self.shift+self.H-1 then self:redraw() end
374
end
375
376
function TList:sort(comp)
377
  comp=comp or function(list,i,j) return list.lines[j]<list.lines[i] end
378
  for i=1,#self.lines-1 do
379
    for j=i+1,#self.lines do
380
	  if comp(self,i,j) then
381
	    if self.index==i then self.index=j
382
		elseif self.index==j then self.index=i end
383
	    self.lines[i],self.lines[j]=self.lines[j],self.lines[i]
384
	    self.items[i],self.items[j]=self.items[j],self.items[i]
385
	  end
386
	end
387
  end
388
  self:redraw()
389
end
390
391
function TList:touch(x, y, btn, user)
392
  local b= self.border==0 and 0 or 1
393
  if x>b and x<=self.W-b and y>b and y<=self.H-b and btn==0 then
394
    local i=self.shift+y-b
395
    if self.index~=i and self.lines[i] then
396
      self.index=i
397
	  self:redraw()
398
	  if self.onChange then self:onChange(self.lines[i],self.items[i],user) end
399
	end
400
  end
401
end
402
403
function TList:scroll(x, y, sh, user)
404
  local b= self.border==0 and 0 or 1
405
  self.shift=self.shift-sh
406
  if self.shift>#(self.lines)-self.H+2*b then self.shift=#(self.lines)-self.H+2*b end
407
  if self.shift<0 then self.shift=0 end
408
  self:redraw()
409
end
410
411
function TComponent:addList(left, top, onChange)
412
  local obj={left=left, top=top, lines={}, items={}, onChange=onChange}
413
  self:makeChild(obj)
414
  return setmetatable(obj,TList)
415
end
416
417
local work
418
---------Невизуальные компоненты--------
419
local TInvisible=setmetatable({W=10, H=3, border=2, draw=function() end},TComponent)
420
TInvisible.__index=TInvisible
421
------------------Event-----------------
422
local TEvent=setmetatable({type=function() return "Event" end},TInvisible)
423
TEvent.__index=TEvent
424
425
function TEvent:run()
426
  if self.onEvent then forms.listen(self.eventName, self.onEvent) end
427
end
428
429
function TEvent:stop()
430
  forms.ignore(self.eventName, self.onEvent)
431
end
432
433
function TComponent:addEvent(eventName, onEvent)
434
  local obj={eventName=eventName, onEvent=onEvent}
435
  self:makeChild(obj)
436
  setmetatable(obj,TEvent)
437
  obj:run()
438
  return obj
439
end
440
441
------------------Timer-----------------
442
local TTimer=setmetatable({Enabled=true, type=function() return "Timer" end},TInvisible)
443
TTimer.__index=TTimer
444
445
function TTimer:run()
446
  self.Enabled=nil
447
  if self.onTime then
448
    self.timerId=event.timer(self.interval,
449
	function ()
450
	  if self.Enabled and work then
451
	    self.onTime(self)
452
	  else
453
		self:stop()
454
	  end
455
	end,
456
	math.huge
457
	)
458
  end
459
end
460
function TTimer:stop()
461
  self.Enabled=false
462
  event.cancel(self.timerId)
463
end
464
465
function TComponent:addTimer(interval, onTime)
466
  local obj={interval=interval, onTime=onTime}
467
  self:makeChild(obj)
468
  setmetatable(obj,TTimer)
469
  obj:run()
470
  return obj
471
end
472
473
-----------Обработчик событий-----------
474
local listeners={}
475
476
function forms.listen(name, callback)
477
  checkArg(1, name, "string")
478
  checkArg(2, callback, "function")
479
  if listeners[name] then
480
    for i = 1, #listeners[name] do
481
      if listeners[name][i] == callback then
482
        return false
483
      end
484
    end
485
  else
486
    listeners[name] = {}
487
  end
488
  table.insert(listeners[name], callback)
489
  return true
490
end
491
492
function forms.ignore(name, callback)
493
  checkArg(1, name, "string")
494
  checkArg(2, callback, "function")
495
  if listeners[name] then
496
    for i = 1, #listeners[name] do
497
      if listeners[name][i] == callback then
498
        table.remove(listeners[name], i)
499
        if #listeners[name] == 0 then
500
          listeners[name] = nil
501
        end
502
        return true
503
      end
504
    end
505
  end
506
  return false
507
end
508
509
function forms.ignoreAll()
510
  listeners={}
511
end
512
----------------------------------------
513
514
function forms.run(form)
515
  work=true
516
  local Fc, Bc = gpu.getForeground(), gpu.getBackground()
517
  activeForm=form
518
  activeForm:draw()
519
  while work do
520
    local ev,adr,x,y,btn,user=event.pull()
521
    if mouseEv[ev] and adr==gpu.getScreen() then activeForm:mouseEv(ev,x,y,btn,user) end
522
	if listeners[ev] then
523
	  for i=1,#listeners[ev] do listeners[ev][i](ev,adr,x,y,btn,user) end
524
    end
525
	if listeners[""] then
526
	  for i=1,#listeners[""] do listeners[""][i](ev,adr,x,y,btn,user) end
527
    end
528
  end
529
  gpu.setForeground(Fc)
530
  gpu.setBackground(Bc)
531
  forms.ignoreAll()
532
end
533
534
function forms.stop()
535
  work=false
536
end
537
538
return forms