View difference between Paste ID: 5Gt85MDp and 6JsWa9Bj
SHOW: | | - or go back to the newest paste.
1
--  Copyright Thomas Bogue 2013
2
--  This program is free software: you can redistribute it and/or modify
3
--  it under the terms of the GNU Lesser General Public License as published by
4
--  the Free Software Foundation, either version 3 of the License, or
5
--  (at your option) any later version.
6
 
7
--  This program is distributed in the hope that it will be useful,
8
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
9
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
--  GNU Lesser General Public License for more details.
11
 
12
--  You should have received a copy of the GNU Lesser General Public License
13
--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
14
 
15
monitor=nil
16
defaultButtonBackground=colors.blue
17
defaultButtonForeground=colors.white
18
terminalBackground=colors.black
19
defaultSliderForeground=colors.white
20
defaultSliderBackground=colors.gray
21
defaultFlagMonitorForeground=colors.white
22
defaultProgressBarForeground=colors.white
23
defaultProgressBarBackground=colors.gray
24
redrawNeeded=false
25
 
26
function version()
27
  return "widget library v1.1"
28
end
29
 
30
local function nop()
31
end
32
 
33
function getMonitor()
34
  return monitor
35
end
36
 
37
function setMonitor(m)
38
  monitor=m
39
end
40
 
41
function getDefaultMonitor()
42
  --print("searching for advanced monitor...")
43
  facings={"top","bottom","right","left","front","back"}
44
  for key,facing in pairs(facings) do
45
    mon=peripheral.wrap(facing)
46
    --print("looking for an advanced monitor " .. facing .. " " .. tostring(mon))
47
    if (mon~=nil) then
48
      --print("peripheral found")
49
      if (mon.setTextColor~=nil) then
50
        --print("monitor found on ".. facing)
51
        return mon
52
      end
53
    end
54
  end
55
  --print("no advanced monitor found.  Is this an advanced computer?")
56
  if (term.isColor()) then
57
    print("using the terminal as an advanced computer")
58
    return(term)
59
  else
60
    error("no advanced monitor or advanced computer found.  This program requires advanced interface hardward.  Please install either an advanced computer or an advanced monitor",0)
61
  end
62
end
63
 
64
function newWidget()
65
  widget={}
66
  widget.draw=nop -- draw(widget)
67
  widget.onClick=nop -- onClick(widget,x,y)
68
  widget.onResize=nop -- onResize(widget)
69
  widget.hWeight=1
70
  widget.vWeight=1
71
  widget.width=nil
72
  widget.height=nil
73
  widget.x1=nil
74
  widget.y1=nil
75
  widget.x2=nil
76
  widget.y2=nil
77
 
78
  function widget.size(widget)
79
    if (monitor==nil) then
80
      monitor=getDefaultMonitor()
81
    end
82
    widget.width,widget.height=monitor.getSize()
83
    widget.x1=1
84
    widget.y1=1
85
    widget.x2=widget.width
86
    widget.y2=widget.height
87
    widget.onResize(widget)
88
    widget.sized=true
89
  end
90
  
91
  function widget.run(widget)
92
    if (widget==null) then
93
      error("invalid call to member function widget:run.  Did you type widget.run instead?",2)
94
    end
95
    if (widget.sized==true) then
96
    else
97
      widget:size()
98
    end
99
    monitor.setBackgroundColor(terminalBackground)
100
    monitor.clear()
101
    widget:draw()
102
    widget.quit=false
103
    while not widget.quit do
104
      if (monitor==term) then
105
        event,side,x,y=os.pullEvent("mouse_click")
106
      else
107
        event,side,x,y=os.pullEvent("monitor_touch")
108
      end
109
      widget:processEvent(event,side,x,y)
110
    end
111
    monitor.setBackgroundColor(terminalBackground)
112
    monitor.clear()
113
  end
114
 
115
  function widget.processEvent(widget,event,side,x,y)
116
    if (event=="monitor_touch" or event=="mouse_click") then
117
      widget:onClick(x,y)
118
      if (redrawNeeded) then
119
        monitor.setBackgroundColor(terminalBackground)
120
        monitor.clear()
121
        widget:draw()
122
      end
123
    end
124
  end
125
  
126
  return(widget)
127
end
128
 
129
function newContainer()
130
  container=newWidget()
131
  container.children={}
132
  container.vSpacingWeight=.1
133
  container.hSpacingWeight=.1
134
 
135
  function container.draw(container)
136
    for key,child in pairs(container.children) do
137
      child:draw()
138
    end
139
  end
140
 
141
  function container.onClick(container,x,y)
142
    for key,child in pairs(container.children) do
143
      if x>=child.x1 and x<=child.x2 and y>=child.y1 and y<=child.y2 then
144
        child:onClick(x,y)
145
      end
146
    end
147
  end
148
  
149
  function container.resizeChildren(container)
150
    for key,child in pairs(container.children) do
151
      child:onResize()
152
    end
153
  end
154
 
155
  container.onResize=resizeChildren
156
  
157
  function container.add(container,widget)
158
    if (widget==nil) then
159
      error("attempt to add nil widget to a container.  Did you perhaps type row.add when you meant row:add?  Or column.add when you meant column:add?",2)
160
    end
161
    container.children[#container.children+1]=widget
162
  end  
163
  
164
  return(container)
165
end
166
 
167
function newButton(label)
168
  button=newWidget()
169
  button.label=label
170
  button.backgroundColor=defaultButtonBackground
171
  button.foregroundColor=defaultButtonForeground
172
  button.labelX=nil
173
  button.labelY=nil
174
 
175
  function button.drawFull(button)
176
    button.labelX=math.floor((button.x1+button.x2-button.label:len())/2)
177
    button.labelY=(button.y1+button.y2)/2
178
    monitor.setBackgroundColor(button.backgroundColor)
179
    monitor.setTextColor(button.foregroundColor)
180
    for x=button.x1,button.x2 do
181
      for y=button.y1,button.y2 do
182
        monitor.setCursorPos(x,y)
183
        monitor.write(" ")
184
      end
185
    end
186
    monitor.setCursorPos(button.labelX,button.labelY)
187
    monitor.write(button.label)
188
  end
189
  
190
  function button.drawOutline(button)
191
    button.labelX=math.floor((button.x1+button.x2-button.label:len())/2)
192
    button.labelY=(button.y1+button.y2)/2
193
    monitor.setBackgroundColor(button.backgroundColor)
194
    for x=button.x1,button.x2 do
195
      monitor.setCursorPos(x,button.y1)
196
      monitor.write(" ")
197
      monitor.setCursorPos(x,button.y2)
198
      monitor.write(" ")
199
    end
200
    for y=button.y1+1,button.y2-1 do
201
      monitor.setCursorPos(button.x1,y)
202
      monitor.write(" ")
203
      monitor.setCursorPos(button.x2,y)
204
      monitor.write(" ")
205
    end
206
    monitor.setBackgroundColor(terminalBackground)
207
    for x=button.x1+1,button.x2-1 do
208
      for y=button.y1+1,button.y2-1 do
209
        monitor.setCursorPos(x,y)
210
        monitor.write(" ")
211
      end
212
    end
213
    monitor.setTextColor(button.foregroundColor)
214
    monitor.setCursorPos(button.labelX,button.labelY)
215
    monitor.write(button.label)
216
  end
217
  
218
  function button.blink(button)
219
    button:drawOutline()
220
    os.sleep(.1)
221
    button:draw()
222
  end
223
  
224
  button.draw=button.drawFull
225
  return(button)
226
end
227
 
228
function newRow()
229
  row=newContainer()
230
 
231
  function row.onResize(row)
232
    totalWeight=row.hSpacingWeight*(#row.children-1)
233
    for key,child in pairs(row.children) do
234
      totalWeight=totalWeight+child.hWeight
235
    end
236
    widths={}
237
    totalWidgetWidth=0
238
    for key,child in pairs(row.children) do
239
      widths[key]=math.floor(row.width*child.hWeight/totalWeight)
240
      totalWidgetWidth=totalWidgetWidth+widths[key]
241
    end
242
    remainingWidth=row.width-totalWidgetWidth
243
    if (#row.children>=1) then
244
      separatorWidth=1
245
    else
246
      separatorWidth=math.floor(remainingWidth/(#row.children-1))
247
    end
248
    currentPosition=row.x1 + math.floor((row.width-totalWidgetWidth-separatorWidth*(#row.children-1))/2)
249
    for key,child in pairs(row.children) do
250
      child.x1=currentPosition
251
      child.y1=row.y1
252
      currentPosition=currentPosition+widths[key]-1
253
      child.x2=currentPosition
254
      child.y2=row.y2
255
      currentPosition=currentPosition+separatorWidth+1
256
      child.width=widths[key]
257
      child.height=row.height
258
    end
259
    row:resizeChildren()
260
  end
261
 
262
  return(row)
263
end
264
 
265
function newColumn()
266
  column=newContainer()
267
 
268
  function column.onResize(column)
269
    totalWeight=column.vSpacingWeight*(#column.children-1)
270
    for key,child in pairs(column.children) do
271
      totalWeight=totalWeight+child.vWeight
272
    end
273
    widths={}
274
    totalWidgetWidth=0
275
    for key,child in pairs(column.children) do
276
      widths[key]=math.floor(column.height*child.vWeight/totalWeight)
277
      totalWidgetWidth=totalWidgetWidth+widths[key]
278
    end
279
    remainingWidth=column.height-totalWidgetWidth
280
    if (#column.children>=1) then
281
      separatorWidth=1
282
    else
283
      separatorWidth=math.floor(remainingWidth/(#column.children-1))
284
    end
285
    currentPosition=column.y1 + math.floor((column.height-totalWidgetWidth-separatorWidth*(#column.children-1))/2)
286
    for key,child in pairs(column.children) do
287
      child.x1=column.x1
288
      child.y1=currentPosition
289
      currentPosition=currentPosition+widths[key]-1
290
      child.x2=column.x2
291
      child.y2=currentPosition
292
      child.width=column.width
293
      child.height=widths[key]
294
      currentPosition=currentPosition+separatorWidth+1
295
    end
296
    column:resizeChildren()
297
  end
298
 
299
  return(column)
300
end
301
 
302
function newToggleButton(label)
303
  button=newButton(label)
304
  function button.toggle(button)
305
    button.state=not button.state
306
    button:draw()
307
  end
308
  
309
  function button.draw(button)
310
    if button.state then
311
      button:drawFull()
312
    else
313
      button:drawOutline()
314
    end
315
  end
316
 
317
  button.onClick=button.toggle
318
  button.state=false
319
  return(button)
320
end
321
 
322
function newLabel(text)
323
  label=newWidget()
324
  label.text=text
325
  label.textColor=defaultButtonForeground
326
  function label.draw(label)
327
    leftMargin=math.floor((label.width-label.text:len())/2)
328
    rightMargin=label.width-label.text:len()-leftMargin
329
    y=math.floor((label.y1+label.y2)/2)
330
    paddedText=string.rep(" ",leftMargin)..label.text..string.rep(" ",rightMargin)
331
    monitor.setTextColor(label.textColor)
332
    monitor.setCursorPos(label.x1,y)
333
    monitor.write(paddedText)
334
  end
335
 
336
  return(label)
337
end
338
 
339
function newSlider(min,max)
340
  slider=newWidget()
341
  slider.min=min
342
  slider.max=max
343
  slider.value=min
344
  slider.left=nil
345
  slider.foregroundColor=defaultSliderForeground
346
  slider.backgroundColor=defaultSliderBackground
347
  
348
  function slider.draw(slider)
349
    range=slider.max-slider.min+1
350
    slider.left=math.floor((slider.x1+slider.x2-range)/2)
351
    margin=slider.left-slider.x1
352
    y=math.floor((slider.y1+slider.y2)/2)
353
    left=slider.value-slider.min+1
354
    right=slider.max-slider.value
355
    monitor.setCursorPos(slider.x1,y)
356
    monitor.setBackgroundColor(terminalBackground)
357
    monitor.write(string.rep(" ",margin))
358
    monitor.setBackgroundColor(slider.foregroundColor)
359
    monitor.write(string.rep(" ",left))
360
    monitor.setBackgroundColor(slider.backgroundColor)
361
    monitor.write(string.rep(" ",right))
362
    monitor.setBackgroundColor(terminalBackground)
363
    monitor.setTextColor(slider.foregroundColor)
364
    tag=" "..tostring(slider.value*25)
365
    roomleft=slider.width-margin-range-tag:len()
366
    if (roomleft>=0) then
367
      monitor.write(tag..string.rep(" ",slider.width-margin-tag:len()-range))
368
    end
369
  end
370
  
371
  function slider.onClick(slider,x,y)
372
    slider.value=x-slider.left+slider.min
373
    if (slider.value<slider.min) then
374
      slider.value=slider.min
375
    else
376
      if (slider.value>slider.max) then
377
        slider.value=slider.max
378
      end
379
    end
380
    slider:draw()
381
  end
382
 
383
  slider.adjust=slider.onClick
384
  return(slider)
385
end
386
  
387
function newFlagMonitor(side) -- signal display widget
388
  flag=newWidget()
389
  flag.side=side
390
  flag.flags=0
391
  flag.sized=false
392
  flag.foregroundColor=defaultFlagMonitorForeground
393
  
394
  function flag.get(flag,n)
395
    x=bit.blshift(1,n-1)
396
    y=bit.band(flag.flags,x)
397
    z=(y~=0)
398
    return z
399
  end
400
  
401
  function flag.set(flag,n,value)
402
    if (value==nil) then
403
      value=true
404
    end
405
    if (value) then
406
      flag.flags=bit.bor(flag.flags,bit.blshift(1,n-1))
407
    else
408
      flag.flags=bit.band(flag.flags,bit.bnot(bit.blshift(1,n-1)))
409
    end
410
    flag:transmit()
411
  end
412
  
413
  function flag.clear(flag,n)
414
    flag.flags=bit.band(flag.flags,bit.bnot(bit.blshift(1,n-1)))
415
    flag:transmit()
416
  end
417
  
418
  function flag.toggle(flag,n)
419
    flag.flags=bit.bxor(flag.flags,bit.blshift(1,n-1))
420
    flag:transmit()
421
  end
422
  
423
  function flag.transmit(flag)
424
    --print("transmitting " .. flag.flags .. " to " .. flag.side)
425
    redstone.setBundledOutput(flag.side,flag.flags)
426
    flag:draw()
427
  end
428
  
429
  function flag.draw(flag)
430
    if ( not flag.sized ) then
431
      return
432
    end
433
    ylocal=math.floor((flag.y1+flag.y2)/2)
434
    xlocal=math.floor((flag.x1+flag.x2-8)/2)
435
    monitor.setCursorPos(xlocal,ylocal)
436
    for i=1,16 do
437
      color=bit.blshift(1,i-1)
438
      if (flag:get(i)) then
439
        monitor.setBackgroundColor(color)
440
        monitor.setTextColor(flag.foregroundColor)
441
        monitor.write(" ")
442
      else
443
        monitor.setBackgroundColor(terminalBackground)
444
        monitor.setTextColor(color)
445
        monitor.write(".")
446
      end
447
      if (i==8) then
448
        monitor.setCursorPos(xlocal,ylocal+1)
449
      end
450
    end
451
  end
452
  
453
  function flag.onResize(flag)
454
    flag.sized=true
455
  end
456
  
457
  return(flag)
458
end
459
 
460
function newChooser(choice,options)
461
  chooser=newButton(choice)
462
  chooser.options=options
463
 
464
  function chooser.choose(chooser)
465
    if (chooser.tab==nil) then
466
      chooser:formTab()
467
    end
468
    chooser.tab:run()
469
    redrawNeeded=true
470
  end
471
  
472
  function chooser.buttonOnClick(button)
473
    button:blink()
474
    button.chooser.label=button.label
475
    button.chooser.tab.quit=true
476
  end
477
  
478
  function chooser.formTab(chooser)
479
    chooser.tab=newRow()
480
    width,height=monitor.getSize()
481
    buttonsPerColumn=math.floor(height/4)
482
    numButtons=#(chooser.options)
483
    numColumns=math.ceil(numButtons/buttonsPerColumn)
484
    columnNum=1
485
    rowNum=1
486
    currentColumn=newColumn()
487
    for key,option in pairs(chooser.options) do
488
      if (rowNum>buttonsPerColumn) then
489
        rowNum=1
490
        columnNum=columnNum+1
491
        chooser.tab:add(currentColumn)
492
        currentColumn=newColumn()
493
      end
494
      choiceButton=newButton(option)
495
      choiceButton.vWeight=.3
496
      choiceButton.chooser=chooser
497
      choiceButton.onClick=chooser.buttonOnClick
498
      currentColumn:add(choiceButton)
499
      rowNum=rowNum+1
500
    end
501
    chooser.tab:add(currentColumn)
502
  end
503
 
504
  chooser.onClick=chooser.choose
505
  chooser.tab=nil
506
  return(chooser)
507
end
508
 
509
function newProgressBar(max)
510
  bar=newWidget()
511
  if (max==nil) then
512
    bar.max=1.0
513
  else
514
    bar.max=max
515
  end
516
  bar.value=0
517
  bar.foreground=defaultProgressBarForeground
518
  bar.background=defaultProgressBarBackground
519
  
520
  function bar.draw(bar)
521
    y=math.floor((bar.y1+bar.y2)/2)
522
    xOn=math.floor(bar.width*bar.value/bar.max)
523
    xOff=bar.width-xOn
524
    monitor.setBackgroundColor(bar.foreground)
525
    monitor.setCursorPos(bar.x1,y)
526
    monitor.write(string.rep(" ",xOn))
527
    monitor.setBackgroundColor(bar.background)
528
    monitor.write(string.rep(" ",xOff))
529
  end
530
 
531
  function bar.setValue(bar,value)
532
    bar.value=value
533
    bar:draw()
534
  end
535
 
536
  function bar.getValue(bar)
537
    return(bar.value)
538
  end
539
 
540
  return(bar)
541
end