From fd6bb84079555d5788cafaa4c8b9c8f4f514ac4d Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 23 Mar 2021 11:13:11 -0500 Subject: [PATCH 1/5] add FlipInput widget --- LICENSES/OFL.txt | 97 +++ .../widgets/flip_input.py | 625 ++++++++++++++++++ docs/api.rst | 5 + docs/examples.rst | 9 + .../displayio_layout_flip_input_simpletest.py | 131 ++++ .../fonts/DSEG14Classic-Regular-64-ModS.pcf | Bin 0 -> 41204 bytes .../DSEG14Classic-Regular-64-ModS.pcf.license | 3 + 7 files changed, 870 insertions(+) create mode 100644 LICENSES/OFL.txt create mode 100644 adafruit_displayio_layout/widgets/flip_input.py create mode 100755 examples/displayio_layout_flip_input_simpletest.py create mode 100755 examples/fonts/DSEG14Classic-Regular-64-ModS.pcf create mode 100644 examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license diff --git a/LICENSES/OFL.txt b/LICENSES/OFL.txt new file mode 100644 index 0000000..f1a20ac --- /dev/null +++ b/LICENSES/OFL.txt @@ -0,0 +1,97 @@ +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/adafruit_displayio_layout/widgets/flip_input.py b/adafruit_displayio_layout/widgets/flip_input.py new file mode 100644 index 0000000..fd97f55 --- /dev/null +++ b/adafruit_displayio_layout/widgets/flip_input.py @@ -0,0 +1,625 @@ +# SPDX-FileCopyrightText: 2021 Kevin Matocha +# +# SPDX-License-Identifier: MIT +""" + +`flip_input` +================================================================================ +A flip style input selector. + +* Author(s): Kevin Matocha + +Implementation Notes +-------------------- + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +import gc +import time +import displayio +from terminalio import FONT + +from adafruit_display_shapes.triangle import Triangle + +from adafruit_display_text import bitmap_label +from adafruit_displayio_layout.widgets.widget import Widget +from adafruit_displayio_layout.widgets.control import Control + +# pylint: disable=reimported + +# select the two "easing" functions to use for animations +from adafruit_displayio_layout.widgets.easing import back_easeinout as easein +from adafruit_displayio_layout.widgets.easing import back_easeinout as easeout + +# pylint: disable=too-many-arguments, too-many-branches, too-many-statements +# pylint: disable=too-many-locals, too-many-instance-attributes + + +class FlipInput(Widget, Control): + """A flip style input selector. + + :param int x: pixel position + :param int y: pixel position + + :param displayio.Display display: the display where the widget will be displayed + :param value_list: the list of strings that will be displayed + :type value_list: List[str] + :param Font font: the font used for the text (defaults to ``terminalio.FONT``) + :param int color: the color used for the font (default is 0xFFFFFF) + :param int value: the index into the value_list that is initially displayed + (default is 0) + + :param int arrow_touch_padding: additional pixel space surrounding the arrows + that responds to touch inputs, in pixels (default = 0) + :param int arrow_color: the color used for the arrow fill (default is `None`) + :param int arrow_outline: the color used for the arrow outline (default is `None`) + :param int arrow_height: the height of the arrows, in pixels (default is `None`) + :param int arrow_width: the width of the arrows, in pixels (default is `None`) + :param int alt_touch_padding: additional padding on the non-arrow sides of the + widget where touch-response is accepted, in pixels (default = 0) + :param Boolean horizontal: set to `True` to display arrows are in the horizontal + direction, set `False` for arrows in the vertical direction (default = `True`) + :param float animation_time: duration for the animation during flipping between + values (default is `None`, no animation) + + """ + + def __init__( + self, + display, + *, + value_list=None, + font=FONT, + color=0xFFFFFF, + value=0, # initial value, index into the value_list + arrow_touch_padding=0, # touch padding on the arrow sides of the Widget + arrow_color=None, + arrow_outline=None, + arrow_height=None, + arrow_width=None, + alt_touch_padding=0, # touch padding on the non-arrow sides of the Widget + horizontal=True, + animation_time=None, + **kwargs, + ): + + super().__init__(**kwargs, max_size=4) + # Group elements for the FlipInput. + # 0. The text + # 1. The group holding the temporary scroll bitmap + # 2. Up arrow: Triangle + # 3. Down arrow: Triangle + + # initialize the Control superclass + + # pylint: disable=bad-super-call + super(Control, self).__init__() + + self.value_list = value_list + self._value = value + + self._color = color + self._font = font + # preload the glyphs + + self._arrow_touch_padding = arrow_touch_padding + self._alt_touch_padding = alt_touch_padding + + self._horizontal = horizontal + self._display = display + + self._animation_time = animation_time + + # Find the maximum bounding box of the text and determine the + # baseline (x,y) start point (top, left) + + left = None + right = None + top = None + bottom = None + + for this_value in value_list: + xposition = 0 + + for i, character in enumerate(this_value): + glyph = self._font.get_glyph(ord(character)) + + if ( + i == 0 + ): # if it's the first character in the string, check the left value + if left is None: + left = glyph.dx + else: + left = min(left, glyph.dx) + + if right is None: + right = max( + xposition + glyph.dx + glyph.width, xposition + glyph.shift_x + ) + else: + right = max( + right, + xposition + glyph.dx + glyph.width, + xposition + glyph.shift_x, + ) # match bitmap_label + + if top is None: + top = -(glyph.height + glyph.dy) + else: + top = min(top, -(glyph.height + glyph.dy)) + + if bottom is None: + bottom = -glyph.dy + else: + bottom = max(bottom, -glyph.dy) + + xposition = xposition + glyph.shift_x + + self._bounding_box = [0, 0, right - left, bottom - top] + + # Create the text label + self._label = bitmap_label.Label( + text=value_list[value], + font=self._font, + color=self._color, + base_alignment=True, + background_tight=True, + ) + self._label.x = -1 * left + self._label.y = -1 * top + + self.append(self._label) # add the label to the self Group + + # set the touch_boundary including the touch_padding + + if horizontal: # horizontal orientation, add arrow padding to x-dimension and + # alt_padding to y-dimension + self.touch_boundary = [ + self._bounding_box[0] - self._arrow_touch_padding, + self._bounding_box[1] - self._alt_touch_padding, + self._bounding_box[2] + 2 * self._arrow_touch_padding, + self._bounding_box[3] + 2 * self._alt_touch_padding, + ] + else: # vertical orientation, add arrow padding to y-dimension and + # alt_padding to x-dimension + self.touch_boundary = [ + self._bounding_box[0] - self._alt_touch_padding, + self._bounding_box[1] - self._arrow_touch_padding, + self._bounding_box[2] + 2 * self._alt_touch_padding, + self._bounding_box[3] + 2 * self._arrow_touch_padding, + ] + + # create the Up/Down arrows + self._update_position() # call Widget superclass function to reposition + + self._animation_group = displayio.Group( + max_size=1 + ) # holds the animation bitmap + self._animation_group.hidden = True + self.append(self._animation_group) + + # Add the two arrow triangles, if required + + if (arrow_color is not None) or (arrow_outline is not None): + gap = 5 # of pixel gap above/below label + + if horizontal: # horizontal orientation, add left and right arrows + if arrow_height is None: + arrow_height = self._bounding_box[3] + if arrow_width is None: + arrow_width = arrow_touch_padding + + if arrow_width > 0: + mid_point_y = self._bounding_box[1] + self._bounding_box[3] // 2 + self.append( + Triangle( + self._bounding_box[0] - gap, + mid_point_y - arrow_height // 2, + self._bounding_box[0] - gap, + mid_point_y + arrow_height // 2, + self._bounding_box[0] - gap - arrow_width, + mid_point_y, + fill=arrow_color, + outline=arrow_outline, + ) + ) + + self.append( + Triangle( + self._bounding_box[0] + self._bounding_box[2] + gap, + mid_point_y - arrow_height // 2, + self._bounding_box[0] + self._bounding_box[2] + gap, + mid_point_y + arrow_height // 2, + self._bounding_box[0] + + self._bounding_box[2] + + gap + + arrow_width, + mid_point_y, + fill=arrow_color, + outline=arrow_outline, + ) + ) + else: # vertical orientation, add upper and lower arrows + if arrow_height is None: + arrow_height = arrow_touch_padding + if arrow_width is None: + arrow_width = self._bounding_box[2] + + if arrow_height > 0: + mid_point_x = self._bounding_box[0] + self._bounding_box[2] // 2 + self.append( + Triangle( + mid_point_x - arrow_width // 2, + self._bounding_box[1] - gap, + mid_point_x + arrow_width // 2, + self._bounding_box[1] - gap, + mid_point_x, + self._bounding_box[1] - gap - arrow_height, + fill=arrow_color, + outline=arrow_outline, + ) + ) + self.append( + Triangle( + mid_point_x - arrow_width // 2, + self._bounding_box[1] + self._bounding_box[3] + gap, + mid_point_x + arrow_width // 2, + self._bounding_box[1] + self._bounding_box[3] + gap, + mid_point_x, + self._bounding_box[1] + + self._bounding_box[3] + + gap + + arrow_height, + fill=arrow_color, + outline=arrow_outline, + ) + ) + + # Draw function of the current value + + def _update_value(self, new_value, animate=True): + + if ( + (self._animation_time is not None) + and (self._animation_time > 0) # If animation is required + and (animate) + ): + + if ((new_value - self.value) == 1) or ( + (self.value == (len(self.value_list) - 1)) and (new_value == 0) + ): # wrap around + start_position = 0.0 + end_position = 1.0 + else: + start_position = 1.0 + end_position = 0.0 + + self._display.auto_refresh = False + + gc.collect() + + # create the animation bitmap + animation_bitmap = displayio.Bitmap( + self._bounding_box[2], self._bounding_box[3], 2 + ) # color depth 2 + + palette = displayio.Palette(2) + palette.make_transparent(0) + palette[1] = self._color + animation_tilegrid = displayio.TileGrid( + animation_bitmap, pixel_shader=palette + ) + + # add bitmap to the animation_group + self._animation_group.append(animation_tilegrid) + + # store away the initial starting bitmap + start_bitmap = displayio.Bitmap( + self._label.bitmap.width, self._label.bitmap.height, 2 + ) # color depth 2 + start_bitmap.blit(0, 0, self._label.bitmap) + # get the bitmap1 position offsets + bitmap1_offset = [ + self._label.x + self._label.tilegrid.x, + self._label.y + self._label.tilegrid.y, + ] + + # hide the label group. + self.pop(0) + + # update the value label and get the bitmap offsets + self._label.text = str(self.value_list[new_value]) + bitmap2_offset = [ + self._label.x + self._label.tilegrid.x, + self._label.y + self._label.tilegrid.y, + ] + + # animate between old and new bitmaps + _animate_bitmap( + display=self._display, + target_bitmap=animation_bitmap, + bitmap1=start_bitmap, + bitmap1_offset=bitmap1_offset, + bitmap2=self._label.bitmap, + bitmap2_offset=bitmap2_offset, + start_position=start_position, + end_position=end_position, + animation_time=self._animation_time, + horizontal=self._horizontal, + ) + + # unhide the label group + self.insert(0, self._label) + + # hide the animation group + self._animation_group.pop() + # free up memory + del animation_bitmap + del start_bitmap + gc.collect() + + # ensure the display will auto_refresh (likely redundant) + self._display.auto_refresh = True + + else: # Update with no animation + self._display.auto_refresh = False + self._label.text = str(self.value_list[new_value]) + self._display.auto_refresh = True + self._update_position() # call Widget superclass function to reposition + + def contains(self, touch_point): # overrides, then calls Control.contains(x,y) + """Returns True if the touch_point is within the widget's touch_boundary.""" + + ###### + # + # IMPORTANT: The touch_point should be adjusted to local coordinates, by + # offsetting for self.x and self.y before calling the Control superclass function + # + ###### + touch_x = ( + touch_point[0] - self.x + ) # adjust touch position for the local position + touch_y = touch_point[1] - self.y + + return super().contains((touch_x, touch_y, 0)) + + def selected(self, touch_point): + """Response function when Control is selected. Increases value when upper half is pressed + and decreases value when lower half is pressed.""" + + # Adjust for local position of the widget using self.x and self.y + + t_b = self.touch_boundary + + if self._horizontal: + if ( + t_b[0] <= (touch_point[0] - self.x) < (t_b[0] + t_b[2] // 2) + ): # in left half of touch_boundary + self.value = self.value - 1 + + elif ( + (t_b[0] + t_b[2] // 2) <= (touch_point[0] - self.x) <= (t_b[0] + t_b[2]) + ): # in right half of touch_boundary + self.value = self.value + 1 + + else: + if ( + t_b[1] <= (touch_point[1] - self.y) < (t_b[1] + t_b[3] // 2) + ): # in upper half of touch_boundary + self.value = self.value + 1 + + elif ( + (t_b[1] + t_b[3] // 2) <= (touch_point[1] - self.y) <= (t_b[1] + t_b[3]) + ): # in lower half of touch_boundary + self.value = self.value - 1 + + @property + def value(self): + """The value index displayed on the widget. For the setter, the input can + either be an `int` index into the ``value_list`` or + can be a `str` that matches one of the items in the ``value_list``. If `int`, + the value will be set based on the modulus of the input ``new_value``. + + :return: int + """ + + return self._value + + @value.setter + def value(self, new_value): # Set the value based on the index or on the string. + if isinstance(new_value, str): # for an input string, search the value_list + try: + new_value = self.value_list.index(new_value) + except ValueError: + print( + 'ValueError: Value "{}" not found in value_list.'.format(new_value) + ) + return None + + new_value = new_value % len(self.value_list) # Update the value + if new_value != self._value: + self._update_value(new_value) + self._value = new_value + return self._value + + +# draw_position - Draws two bitmaps with offsets. +# Allows values < 0.0 and > 1.0 for "springy" easing functions +def _draw_position( + target_bitmap, + bitmap1, + bitmap1_offset, + bitmap2, + bitmap2_offset, + position=0.0, + horizontal=True, +): + + x_offset1 = bitmap1_offset[0] + y_offset1 = bitmap1_offset[1] + x_offset2 = bitmap2_offset[0] + y_offset2 = bitmap2_offset[1] + + if position == 0.0: + target_bitmap.fill(0) + target_bitmap.blit(x_offset1, y_offset1, bitmap1) + return + if position == 1.0: + target_bitmap.fill(0) + target_bitmap.blit(x_offset2, y_offset2, bitmap2) + return + + if horizontal: + target_bitmap.fill(0) + x_index = round(position * target_bitmap.width) + _blit_constrained(target_bitmap, x_offset1, y_offset1, bitmap1, x1=x_index) + _blit_constrained( + target_bitmap, + target_bitmap.width - x_index + x_offset2, + y_offset2, + bitmap2, + x1=0, + x2=x_index, + ) + + else: + target_bitmap.fill(0) + y_index = round(position * target_bitmap.height) + + _blit_constrained(target_bitmap, x_offset1, y_offset1, bitmap1, y1=y_index) + _blit_constrained( + target_bitmap, + x_offset2, + target_bitmap.height - y_index + y_offset2, + bitmap2, + y1=0, + y2=y_index, + ) + + +# pylint: disable=invalid-name + +# _blit_constrained: Copies bitmaps with constraints to the dimensions +def _blit_constrained(target, x, y, source, x1=None, y1=None, x2=None, y2=None): + if x1 is None: + x1 = 0 + if y1 is None: + y1 = 0 + if x2 is None: + x2 = source.width + if y2 is None: + y2 = source.height + + if x < 0: + x1 -= x # offset the clip region in positive direction + x2 -= x + x = 0 + if x1 < 0: + x = x - x1 + x1 = 0 # move to origin + if x2 > source.width: + x2 = source.width + + if y < 0: + y1 -= y # offset the clip region + y2 -= y + y = 0 + if y1 < 0: + y = y - y1 + y1 = 0 + if y2 > source.height: + y2 = source.height + + if ( + (x > target.width) + or (y > target.height) + or (x1 > source.width) + or (y1 > source.height) + ): + return + + target.blit(x, y, source, x1=x1, y1=y1, x2=x2, y2=y2) + + +# _animate_bitmap - performs animation of scrolling between two bitmaps +def _animate_bitmap( + display, + target_bitmap, + bitmap1, + bitmap1_offset, + bitmap2, + bitmap2_offset, + start_position, + end_position, + animation_time, + horizontal, +): + + max_position = max(start_position, end_position) + min_position = min(start_position, end_position) + start_time = time.monotonic() + + if start_position > end_position: # direction is decreasing: "out" + [bitmap2, bitmap1] = [bitmap1, bitmap2] + [bitmap2_offset, bitmap1_offset] = [bitmap1_offset, bitmap2_offset] + easing_function = easeout # use the "out" easing function + + else: # direction is increasing: "in" + easing_function = easein # use the "in" easing function + + display.auto_refresh = False + _draw_position( + target_bitmap, + bitmap1, + bitmap1_offset, + bitmap2, + bitmap2_offset, + position=start_position, + horizontal=horizontal, + ) + display.auto_refresh = True + + while True: + + target_position = ( + start_position + + (end_position - start_position) + * (time.monotonic() - start_time) + / animation_time + ) + + display.auto_refresh = False + if min_position < target_position < max_position: + display.auto_refresh = False + _draw_position( + target_bitmap, + bitmap1, + bitmap1_offset, + bitmap2, + bitmap2_offset, + position=easing_function(target_position), + horizontal=horizontal, + ) + display.auto_refresh = True + else: + + _draw_position( + target_bitmap, + bitmap1, + bitmap1_offset, + bitmap2, + bitmap2_offset, + position=end_position, + horizontal=horizontal, + ) + + break + display.auto_refresh = True + display.auto_refresh = True diff --git a/docs/api.rst b/docs/api.rst index 8f3cdae..24f6058 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -33,3 +33,8 @@ .. automodule:: adafruit_displayio_layout.widgets.icon_widget :members: :member-order: bysource + + +.. automodule:: adafruit_displayio_layout.widgets.flip_input + :members: + :member-order: bysource diff --git a/docs/examples.rst b/docs/examples.rst index bedc2c9..c512a4f 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -24,3 +24,12 @@ Create multiple sliding switch with various sizes and orientations. .. literalinclude:: ../examples/displayio_layout_switch_multiple.py :caption: examples/displayio_layout_switch_multiple.py :linenos: + +FlipInput simple test +--------------------- + +Create three FlipInput selectors. + +.. literalinclude:: ../examples/displayio_layout_flip_input_simpletest.py + :caption: examples/displayio_layout_flip_input_simpletest.py + :linenos: diff --git a/examples/displayio_layout_flip_input_simpletest.py b/examples/displayio_layout_flip_input_simpletest.py new file mode 100755 index 0000000..ebd486c --- /dev/null +++ b/examples/displayio_layout_flip_input_simpletest.py @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: 2021 Kevin Matocha +# +# SPDX-License-Identifier: MIT +############################# +""" +This is a basic demonstration of a FlipInput widget. +""" + +# pylint: disable=invalid-name + +import time +import board +import displayio +from adafruit_displayio_layout.widgets.flip_input import FlipInput + +from adafruit_bitmap_font import bitmap_font +import adafruit_touchscreen + +display = board.DISPLAY # create the display on the PyPortal, +# otherwise change this to setup the display +# for display chip driver and pinout you have (e.g. ILI9341) + +# setup the touchscreen +ts = adafruit_touchscreen.Touchscreen( + board.TOUCH_XL, + board.TOUCH_XR, + board.TOUCH_YD, + board.TOUCH_YU, + calibration=((5200, 59000), (5800, 57000)), + size=(display.width, display.height), +) + +# Select the font file for use +font_file = "fonts/DSEG14Classic-Regular-64-ModS.pcf" +my_font = bitmap_font.load_font(font_file) + +my_flip1 = FlipInput( + display, + anchor_point=[0.0, 0.0], + anchored_position=[10, 40], + color=0xFF2222, + value_list=[ + "JAN", + "FEB", + "MAR", + "APR", + "MAY", + "JUN", + "JUL", + "AUG", + "SEP", + "OCT", + "NOV", + "DEC", + ], + font=my_font, + arrow_touch_padding=40, + arrow_color=0x333333, + arrow_outline=0x444444, + arrow_height=20, + arrow_width=30, + horizontal=False, + animation_time=0.4, +) + +my_flip2 = FlipInput( + display, + anchor_point=[0.0, 0.0], + anchored_position=[180, 40], + color=0xFF2222, + value_list=["{0:02d}".format(x) for x in range(1, 31 + 1)], + font=my_font, + arrow_touch_padding=40, + arrow_color=0x333333, + arrow_outline=0x444444, + arrow_height=20, + arrow_width=30, + horizontal=False, + animation_time=0.4, +) + +my_flip3 = FlipInput( + display, + anchor_point=[0.5, 1.0], + anchored_position=[320 // 2, 240 - 10], + color=0xFF2222, + value_list=["{0:02d}".format(x) for x in range(1985, 2022, 1)], + font=my_font, + arrow_touch_padding=40, + arrow_color=0x333333, + arrow_outline=0x444444, + arrow_height=30, + arrow_width=20, + horizontal=True, + animation_time=0.6, # add more time since the animation covers a longer distance +) + +# Pick an interesting date to start +# You can set the value by direct integer indexes of the list or by one of the strings +# Here are three ways to set the values +my_flip1.value = 9 # use direct integer indexing to set the value to the 10th month +my_flip2.value = my_flip2.value_list.index("21") # find the index yourself by + # searching the value_list +my_flip3.value = "2015" # or set the value based on a string that is in the value_list + +# Create the group to display and append the FlipInput widgets +my_group = displayio.Group(max_size=3) +my_group.append(my_flip1) +my_group.append(my_flip2) +my_group.append(my_flip3) + +display.show(my_group) # add high level Group to the display +display.auto_refresh = True + +while True: + + p = ts.touch_point + # print("touch_point p: {}".format(p)) # print the touch point + + if p: # if touched, check if any of the widgets was triggered + if my_flip1.contains(p): + my_flip1.selected(p) + time.sleep(0.15) # add a short delay to reduce accidental press + elif my_flip2.contains(p): + my_flip2.selected(p) + time.sleep(0.15) # add a short delay to reduce accidental press + elif my_flip3.contains(p): + my_flip3.selected(p) + time.sleep(0.15) # add a short delay to reduce accidental press + + time.sleep(0.01) # small delay seems to improve touch response diff --git a/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf b/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf new file mode 100755 index 0000000000000000000000000000000000000000..08245e982d931bf786406ba8d46efc53dfdb7579 GIT binary patch literal 41204 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 XO8kqnXlMAW^AosNVXR(*aTxUfK@OgZ literal 0 HcmV?d00001 diff --git a/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license b/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license new file mode 100644 index 0000000..fb6d6c6 --- /dev/null +++ b/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2020 keshikan (https://www.keshikan.net) + +SPDX-License-Identifier: OFL From 206f18819529e20ca67b2e81e8c126644f88d491 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 23 Mar 2021 11:18:56 -0500 Subject: [PATCH 2/5] update OFL license --- LICENSES/OFL.txt | 97 ------------------- .../displayio_layout_flip_input_simpletest.py | 14 +-- .../DSEG14Classic-Regular-64-ModS.pcf.license | 2 +- 3 files changed, 8 insertions(+), 105 deletions(-) delete mode 100644 LICENSES/OFL.txt diff --git a/LICENSES/OFL.txt b/LICENSES/OFL.txt deleted file mode 100644 index f1a20ac..0000000 --- a/LICENSES/OFL.txt +++ /dev/null @@ -1,97 +0,0 @@ -Copyright (c) , (), -with Reserved Font Name . -Copyright (c) , (), -with Reserved Font Name . -Copyright (c) , (). - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/examples/displayio_layout_flip_input_simpletest.py b/examples/displayio_layout_flip_input_simpletest.py index ebd486c..7a099de 100755 --- a/examples/displayio_layout_flip_input_simpletest.py +++ b/examples/displayio_layout_flip_input_simpletest.py @@ -11,10 +11,10 @@ import time import board import displayio +import adafruit_touchscreen +from adafruit_bitmap_font import bitmap_font from adafruit_displayio_layout.widgets.flip_input import FlipInput -from adafruit_bitmap_font import bitmap_font -import adafruit_touchscreen display = board.DISPLAY # create the display on the PyPortal, # otherwise change this to setup the display @@ -92,16 +92,16 @@ arrow_height=30, arrow_width=20, horizontal=True, - animation_time=0.6, # add more time since the animation covers a longer distance + animation_time=0.6, # add more time since the animation covers a longer distance ) # Pick an interesting date to start # You can set the value by direct integer indexes of the list or by one of the strings # Here are three ways to set the values -my_flip1.value = 9 # use direct integer indexing to set the value to the 10th month -my_flip2.value = my_flip2.value_list.index("21") # find the index yourself by - # searching the value_list -my_flip3.value = "2015" # or set the value based on a string that is in the value_list +my_flip1.value = 9 # use direct integer indexing to set the value to the 10th month +my_flip2.value = my_flip2.value_list.index("21") # find the index yourself by +# searching the value_list +my_flip3.value = "2015" # or set the value based on a string that is in the value_list # Create the group to display and append the FlipInput widgets my_group = displayio.Group(max_size=3) diff --git a/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license b/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license index fb6d6c6..bd995cc 100644 --- a/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license +++ b/examples/fonts/DSEG14Classic-Regular-64-ModS.pcf.license @@ -1,3 +1,3 @@ SPDX-FileCopyrightText: 2020 keshikan (https://www.keshikan.net) -SPDX-License-Identifier: OFL +SPDX-License-Identifier: OFL-1.1 From 3a9961dbebcd1940feb6c3e52def36f3a1eaaa64 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 23 Mar 2021 11:25:53 -0500 Subject: [PATCH 3/5] add license file --- LICENSES/OFL-1.1.txt | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 LICENSES/OFL-1.1.txt diff --git a/LICENSES/OFL-1.1.txt b/LICENSES/OFL-1.1.txt new file mode 100644 index 0000000..f1a20ac --- /dev/null +++ b/LICENSES/OFL-1.1.txt @@ -0,0 +1,97 @@ +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (), +with Reserved Font Name . +Copyright (c) , (). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. From f99b104477c0d338f3ec6b3ec40c954eb04c9bc6 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Tue, 23 Mar 2021 11:40:13 -0500 Subject: [PATCH 4/5] fix trailing space --- LICENSES/OFL-1.1.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSES/OFL-1.1.txt b/LICENSES/OFL-1.1.txt index f1a20ac..70e4f16 100644 --- a/LICENSES/OFL-1.1.txt +++ b/LICENSES/OFL-1.1.txt @@ -22,7 +22,7 @@ with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, +fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The From b2650d4469d17391152566cec1283ee5708de7e5 Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Mon, 29 Mar 2021 09:49:10 -0500 Subject: [PATCH 5/5] add state variable and timing for selected/released options to reduce accidental touches --- .../widgets/flip_input.py | 78 +++++++++++++------ 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/adafruit_displayio_layout/widgets/flip_input.py b/adafruit_displayio_layout/widgets/flip_input.py index 7f50168..2498295 100644 --- a/adafruit_displayio_layout/widgets/flip_input.py +++ b/adafruit_displayio_layout/widgets/flip_input.py @@ -73,6 +73,11 @@ class FlipInput(Widget, Control): direction, set `False` for arrows in the vertical direction (default = `True`) :param float animation_time: duration for the animation during flipping between values, in seconds (default is 0.4 seconds), set to 0.0 or `None` for no animation. + :param float cool_down: minimum duration between activations of the widget with a + continuous pressing, this can be used to reduce the chance of accidental multiple + activations, in seconds (default is 0.0 seconds, no delay). Set to -1.0 to require + the button be released and pressed again for activation (Note: This requires calling + the ``released`` function prior to the next call to ``selected``.) """ @@ -94,6 +99,7 @@ def __init__( alt_touch_padding=0, # touch padding on the non-arrow sides of the Widget horizontal=True, animation_time=None, + cool_down=0.0, **kwargs, ): @@ -124,6 +130,9 @@ def __init__( self._display = display self._animation_time = animation_time + self._cool_down = cool_down + self._last_pressed = time.monotonic() + self._pressed = False # state variable # Find the maximum bounding box of the text and determine the # baseline (x,y) start point (top, left) @@ -412,6 +421,15 @@ def _update_value(self, new_value, animate=True): self._display.auto_refresh = True self._update_position() # call Widget superclass function to reposition + def _ok_to_change(self): # checks state variable and timers to determine + # if an update is allowed + if self._cool_down < 0: # if cool_down is negative, require ``released`` + # to be called before next change + return not self._pressed + if (time.monotonic() - self._last_pressed) < self._cool_down: + return False # cool_down time has not transpired + return True + def contains(self, touch_point): # overrides, then calls Control.contains(x,y) """Returns True if the touch_point is within the widget's touch_boundary.""" @@ -429,34 +447,50 @@ def contains(self, touch_point): # overrides, then calls Control.contains(x,y) return super().contains((touch_x, touch_y, 0)) def selected(self, touch_point): - """Response function when Control is selected. Increases value when upper half is pressed - and decreases value when lower half is pressed.""" + """Response function when the Control is selected. Increases value when upper half + is pressed and decreases value when lower half is pressed.""" # Adjust for local position of the widget using self.x and self.y - t_b = self.touch_boundary + if self._ok_to_change(): + t_b = self.touch_boundary - if self._horizontal: - if ( - t_b[0] <= (touch_point[0] - self.x) < (t_b[0] + t_b[2] // 2) - ): # in left half of touch_boundary - self.value = self.value - 1 + if self._horizontal: + if ( + t_b[0] <= (touch_point[0] - self.x) < (t_b[0] + t_b[2] // 2) + ): # in left half of touch_boundary + self.value = self.value - 1 - elif ( - (t_b[0] + t_b[2] // 2) <= (touch_point[0] - self.x) <= (t_b[0] + t_b[2]) - ): # in right half of touch_boundary - self.value = self.value + 1 + elif ( + (t_b[0] + t_b[2] // 2) + <= (touch_point[0] - self.x) + <= (t_b[0] + t_b[2]) + ): # in right half of touch_boundary + self.value = self.value + 1 - else: - if ( - t_b[1] <= (touch_point[1] - self.y) < (t_b[1] + t_b[3] // 2) - ): # in upper half of touch_boundary - self.value = self.value + 1 - - elif ( - (t_b[1] + t_b[3] // 2) <= (touch_point[1] - self.y) <= (t_b[1] + t_b[3]) - ): # in lower half of touch_boundary - self.value = self.value - 1 + else: + if ( + t_b[1] <= (touch_point[1] - self.y) < (t_b[1] + t_b[3] // 2) + ): # in upper half of touch_boundary + self.value = self.value + 1 + + elif ( + (t_b[1] + t_b[3] // 2) + <= (touch_point[1] - self.y) + <= (t_b[1] + t_b[3]) + ): # in lower half of touch_boundary + self.value = self.value - 1 + + self._pressed = True # update the state variable + self._last_pressed = ( + time.monotonic() + ) # value changed, so update cool_down timer + + def released(self): + """Response function when the Control is released. Resets the state variables + for handling situation when ``cool_down`` is < 0 that requires `released()` before + reacting another another `selected()`.""" + self._pressed = False @property def value(self):