kopia lustrzana https://github.com/peterhinch/micropython-nano-gui
Add Textbox widget. Minor API change to Scale.
rodzic
6691d4f790
commit
ec87b5db69
|
@ -0,0 +1,67 @@
|
|||
# tbox.py Test/demo of Textbox widget for nano-gui
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# Usage:
|
||||
# import gui.demos.tbox
|
||||
|
||||
# Initialise hardware and framebuf before importing modules.
|
||||
from color_setup import ssd # Create a display instance
|
||||
|
||||
from gui.core.nanogui import refresh
|
||||
from gui.core.writer import CWriter
|
||||
|
||||
import uasyncio as asyncio
|
||||
from gui.core.colors import *
|
||||
import gui.fonts.arial10 as arial10
|
||||
from gui.widgets.label import Label
|
||||
from gui.widgets.textbox import Textbox
|
||||
|
||||
# Args common to both Textbox instances
|
||||
# Positional
|
||||
pargs = (2, 2, 124, 7) # Row, Col, Width, nlines
|
||||
|
||||
# Keyword
|
||||
tbargs = {'fgcolor' : YELLOW,
|
||||
'bdcolor' : RED,
|
||||
'bgcolor' : DARKGREEN,
|
||||
}
|
||||
|
||||
async def wrap(wri):
|
||||
s = '''The textbox displays multiple lines of text in a field of fixed dimensions. \
|
||||
Text may be clipped to the width of the control or may be word-wrapped. If the number \
|
||||
of lines of text exceeds the height available, scrolling may be performed \
|
||||
by calling a method.
|
||||
'''
|
||||
tb = Textbox(wri, *pargs, clip=False, **tbargs)
|
||||
tb.append(s, ntrim = 100, line = 0)
|
||||
refresh(ssd)
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
if not tb.scroll(1):
|
||||
break
|
||||
refresh(ssd)
|
||||
|
||||
async def clip(wri):
|
||||
ss = ('clip demo', 'short', 'longer line', 'much longer line with spaces',
|
||||
'antidisestablishmentarianism', 'line with\nline break', 'Done')
|
||||
tb = Textbox(wri, *pargs, clip=True, **tbargs)
|
||||
for s in ss:
|
||||
tb.append(s, ntrim = 100) # Default line=None scrolls to show most recent
|
||||
refresh(ssd)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
async def main(wri):
|
||||
await wrap(wri)
|
||||
await clip(wri)
|
||||
|
||||
def test():
|
||||
refresh(ssd) # Initialise and clear display.
|
||||
CWriter.set_textpos(ssd, 0, 0) # In case previous tests have altered it
|
||||
wri = CWriter(ssd, arial10, verbose=False)
|
||||
wri.set_clip(True, True, False)
|
||||
asyncio.run(main(wri))
|
||||
|
||||
test()
|
|
@ -0,0 +1,126 @@
|
|||
# textbox.py Extension to nanogui providing the Textbox class
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2020 Peter Hinch
|
||||
|
||||
# Usage:
|
||||
# from gui.widgets.textbox import Textbox
|
||||
|
||||
from gui.core.nanogui import DObject
|
||||
from gui.core.writer import Writer
|
||||
|
||||
# Reason for no tab support in private/reason_for_no_tabs
|
||||
|
||||
class Textbox(DObject):
|
||||
def __init__(self, writer, row, col, width, nlines, *, bdcolor=None, fgcolor=None,
|
||||
bgcolor=None, clip=True):
|
||||
height = nlines * writer.height
|
||||
devht = writer.device.height
|
||||
devwd = writer.device.width
|
||||
if ((row + height + 2) > devht) or ((col + width + 2) > devwd):
|
||||
raise ValueError('Textbox extends beyond physical screen.')
|
||||
super().__init__(writer, row, col, height, width, fgcolor, bgcolor, bdcolor)
|
||||
self.nlines = nlines
|
||||
self.clip = clip
|
||||
self.lines = []
|
||||
self.start = 0 # Start line for display
|
||||
|
||||
def _add_lines(self, s):
|
||||
width = self.width
|
||||
font = self.writer.font
|
||||
n = -1 # Index into string
|
||||
newline = True
|
||||
while True:
|
||||
n += 1
|
||||
if newline:
|
||||
newline = False
|
||||
ls = n # Start of line being processed
|
||||
col = 0 # Column relative to text area
|
||||
if n >= len(s): # End of string
|
||||
if n > ls:
|
||||
self.lines.append(s[ls :])
|
||||
return
|
||||
c = s[n] # Current char
|
||||
if c == '\n':
|
||||
self.lines.append(s[ls : n])
|
||||
newline = True
|
||||
continue # Line fits window
|
||||
col += font.get_ch(c)[2] # width of current char
|
||||
if col > width:
|
||||
if self.clip:
|
||||
p = s[ls :].find('\n') # end of 1st line
|
||||
if p == -1:
|
||||
self.lines.append(s[ls : n]) # clip, discard all to right
|
||||
return
|
||||
self.lines.append(s[ls : n]) # clip, discard to 1st newline
|
||||
n = p # n will move to 1st char after newline
|
||||
elif c == ' ': # Easy word wrap
|
||||
self.lines.append(s[ls : n])
|
||||
else: # Edge splits a word
|
||||
p = s.rfind(' ', ls, n + 1)
|
||||
if p >= 0: # spacechar in line: wrap at space
|
||||
assert (p > 0), 'space char in position 0'
|
||||
self.lines.append(s[ls : p])
|
||||
n = p
|
||||
else: # No spacechar: wrap at end
|
||||
self.lines.append(s[ls : n])
|
||||
n -= 1 # Don't skip current char
|
||||
newline = True
|
||||
|
||||
def _print_lines(self):
|
||||
if len(self.lines) == 0:
|
||||
return
|
||||
|
||||
dev = self.device
|
||||
wri = self.writer
|
||||
col = self.col
|
||||
row = self.row
|
||||
left = col
|
||||
ht = wri.height
|
||||
wri.setcolor(self.fgcolor, self.bgcolor)
|
||||
# Print the first (or last?) lines that fit widget's height
|
||||
#for line in self.lines[-self.nlines : ]:
|
||||
for line in self.lines[self.start : self.start + self.nlines]:
|
||||
Writer.set_textpos(dev, row, col)
|
||||
wri.printstring(line)
|
||||
row += ht
|
||||
col = left
|
||||
wri.setcolor() # Restore defaults
|
||||
|
||||
def show(self):
|
||||
dev = self.device
|
||||
super().show()
|
||||
self._print_lines()
|
||||
|
||||
def append(self, s, ntrim=None, line=None):
|
||||
self._add_lines(s)
|
||||
if ntrim is None: # Default to no. of lines that can fit
|
||||
ntrim = self.nlines
|
||||
if len(self.lines) > ntrim:
|
||||
self.lines = self.lines[-ntrim:]
|
||||
self.goto(line)
|
||||
|
||||
def scroll(self, n): # Relative scrolling
|
||||
value = len(self.lines)
|
||||
if n == 0 or value <= self.nlines: # Nothing to do
|
||||
return False
|
||||
s = self.start
|
||||
self.start = max(0, min(self.start + n, value - self.nlines))
|
||||
if s != self.start:
|
||||
self.show()
|
||||
return True
|
||||
return False
|
||||
|
||||
def value(self):
|
||||
return len(self.lines)
|
||||
|
||||
def clear(self):
|
||||
self.lines = []
|
||||
self.show()
|
||||
|
||||
def goto(self, line=None): # Absolute scrolling
|
||||
if line is None:
|
||||
self.start = max(0, len(self.lines) - self.nlines)
|
||||
else:
|
||||
self.start = max(0, min(line, len(self.lines) - self.nlines))
|
||||
self.show()
|
Ładowanie…
Reference in New Issue