Skip to content

Commit

Permalink
Merge branch 'main' into audit-datetime
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed May 30, 2023
2 parents a99b4ee + 488b417 commit da29226
Show file tree
Hide file tree
Showing 97 changed files with 3,102 additions and 1,787 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
steps:
- uses: actions/checkout@v3.5.2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.6.0
uses: actions/setup-python@v4.6.1
with:
python-version: ${{ matrix.python-version }}
- name: Install dev dependencies
Expand Down Expand Up @@ -108,7 +108,7 @@ jobs:
- uses: actions/checkout@v3.5.2
with:
fetch-depth: 0
- uses: actions/setup-python@v4.6.0
- uses: actions/setup-python@v4.6.1
with:
# Use latest, so it understands all syntax.
python-version: ${{ env.max_python_version }}
Expand Down Expand Up @@ -163,7 +163,7 @@ jobs:
steps:
- uses: actions/checkout@v3.5.2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.6.0
uses: actions/setup-python@v4.6.1
with:
python-version: ${{ matrix.python-version }}
- name: Get packages
Expand Down Expand Up @@ -237,7 +237,7 @@ jobs:
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4.6.0
uses: actions/setup-python@v4.6.1
if: ${{ matrix.setup-python }}
with:
python-version: "3.X"
Expand Down
2 changes: 1 addition & 1 deletion android/src/toga_android/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,5 @@ def toga_key(event):
modifiers.add(Key.MOD_2)

return {"key": key, "modifiers": modifiers}
except KeyError:
except KeyError: # pragma: nocover
return None
1 change: 1 addition & 0 deletions android/src/toga_android/libs/android/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
View = JavaClass("android/view/View")
ViewGroup__LayoutParams = JavaClass("android/view/ViewGroup$LayoutParams")
View__MeasureSpec = JavaClass("android/view/View$MeasureSpec")
View__OnFocusChangeListener = JavaInterface("android/view/View$OnFocusChangeListener")
View__OnTouchListener = JavaInterface("android/view/View$OnTouchListener")
ViewTreeObserver__OnGlobalLayoutListener = JavaInterface(
"android/view/ViewTreeObserver$OnGlobalLayoutListener"
Expand Down
5 changes: 5 additions & 0 deletions android/src/toga_android/widgets/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def set_text(self, text):
def set_enabled(self, value):
self.native.setEnabled(value)

# Disable programmatic focus, otherwise whether this widget is focusable will depend
# on whether previous tests have generated keyboard input.
def focus(self):
pass

def set_background_color(self, value):
self.set_background_filter(value)

Expand Down
59 changes: 7 additions & 52 deletions android/src/toga_android/widgets/multilinetextinput.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,21 @@
from travertino.size import at_least

from ..libs.android.text import InputType, TextWatcher
from ..libs.android.text import InputType
from ..libs.android.view import Gravity
from ..libs.android.widget import EditText
from .label import TextViewWidget
from .textinput import TextInput


class TogaTextWatcher(TextWatcher):
def __init__(self, impl):
super().__init__()
self.interface = impl.interface

def beforeTextChanged(self, _charSequence, _start, _count, _after):
pass

def afterTextChanged(self, _editable):
self.interface.on_change(None)

def onTextChanged(self, _charSequence, _start, _before, _count):
pass


class MultilineTextInput(TextViewWidget):
class MultilineTextInput(TextInput):
def create(self):
self.native = EditText(self._native_activity)
self.native.setInputType(
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE
super().create(
input_type=InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE,
handle_confirm=False,
handle_focus=False,
)
self.native.addTextChangedListener(TogaTextWatcher(self))
self.cache_textview_defaults()

def get_value(self):
return str(self.native.getText())

def set_value(self, value):
self.native.setText(value)

def get_readonly(self):
return not self.native.isFocusable()

def set_readonly(self, readonly):
if readonly:
# Implicitly calls setFocusableInTouchMode(False)
self.native.setFocusable(False)
else:
# Implicitly calls setFocusable(True)
self.native.setFocusableInTouchMode(True)

def get_placeholder(self):
return str(self.native.getHint())

def set_placeholder(self, value):
self.native.setHint(value)

def set_alignment(self, value):
self.set_textview_alignment(value, Gravity.TOP)

def set_background_color(self, value):
# This causes any custom color to hide the bottom border line, but it's better
# than set_background_filter, which affects *only* the bottom border line.
self.set_background_simple(value)

def rehint(self):
self.interface.intrinsic.width = at_least(self.interface._MIN_WIDTH)
self.interface.intrinsic.height = at_least(self.interface._MIN_HEIGHT)
Expand Down
7 changes: 4 additions & 3 deletions android/src/toga_android/widgets/passwordinput.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

class PasswordInput(TextInput):
def create(self):
super().create()
self.native.setInputType(
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
super().create(
input_type=(
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD
)
)
5 changes: 5 additions & 0 deletions android/src/toga_android/widgets/slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ def _load_tick_drawable(self):
Slider.TICK_DRAWABLE = attrs.getDrawable(0)
attrs.recycle()

# Disable programmatic focus, otherwise whether this widget is focusable will depend
# on whether previous tests have generated keyboard input.
def focus(self):
pass

def rehint(self):
self.native.measure(
View__MeasureSpec.UNSPECIFIED, View__MeasureSpec.UNSPECIFIED
Expand Down
5 changes: 5 additions & 0 deletions android/src/toga_android/widgets/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ def get_value(self):
def set_value(self, value):
self.native.setChecked(bool(value))

# Disable programmatic focus, otherwise whether this widget is focusable will depend
# on whether previous tests have generated keyboard input.
def focus(self):
pass

def rehint(self):
if not self.native.getLayoutParams():
return
Expand Down
107 changes: 69 additions & 38 deletions android/src/toga_android/widgets/textinput.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
from toga_android.keys import toga_key

from ..libs.android.text import InputType, TextWatcher
from ..libs.android.view import Gravity, OnKeyListener, View__MeasureSpec
from ..libs.android.view import (
Gravity,
OnKeyListener,
View__MeasureSpec,
View__OnFocusChangeListener,
)
from ..libs.android.widget import EditText
from .label import TextViewWidget

Expand All @@ -18,8 +23,8 @@ def beforeTextChanged(self, _charSequence, _start, _count, _after):
pass

def afterTextChanged(self, _editable):
if self.interface.on_change:
self.interface.on_change(widget=self.interface)
self.interface.on_change(None)
self.interface._validate()

def onTextChanged(self, _charSequence, _start, _before, _count):
pass
Expand All @@ -32,56 +37,88 @@ def __init__(self, impl):
self.interface = impl.interface

def onKey(self, _view, _key, _event):
try:
key_pressed = toga_key(_event)["key"].value
if (key_pressed == "<enter>" or key_pressed == "numpad:enter") and int(
_event.getAction()
) == 1:
event_info = toga_key(_event)
if event_info is None:
pass # pragma: nocover
else:
key_pressed = event_info["key"].value
if (key_pressed == "<enter>" or key_pressed == "numpad:enter") and (
int(_event.getAction()) == 1
):
self.interface.on_confirm(None)
return False
except TypeError:
return False
return False


class TogaFocusListener(View__OnFocusChangeListener):
def __init__(self, impl):
super().__init__()
self.impl = impl
self.interface = impl.interface

def onFocusChange(self, view, has_focus):
if has_focus:
self.interface.on_gain_focus(None)
else:
self.interface.on_lose_focus(None)


class TextInput(TextViewWidget):
def create(self):
self._textChangedListener = None
def create(
self,
*,
input_type=InputType.TYPE_CLASS_TEXT,
handle_confirm=True,
handle_focus=True,
):
self.native = EditText(self._native_activity)
self.native.setInputType(InputType.TYPE_CLASS_TEXT)
self.native.setInputType(input_type)
self.cache_textview_defaults()
self._on_key_listener = TogaKeyListener(self)
self.native.setOnKeyListener(self._on_key_listener)

self.native.addTextChangedListener(TogaTextWatcher(self))
if handle_confirm:
self.native.setOnKeyListener(TogaKeyListener(self))
if handle_focus:
self.native.setOnFocusChangeListener(TogaFocusListener(self))

def get_value(self):
return self.native.getText().toString()
return str(self.native.getText())

def set_value(self, value):
self.native.setText(value)

def get_readonly(self):
return not self.native.isFocusable()

def set_readonly(self, value):
self.native.setFocusable(not value)
def set_readonly(self, readonly):
if readonly:
# Implicitly calls setFocusableInTouchMode(False)
self.native.setFocusable(False)
else:
# Implicitly calls setFocusable(True)
self.native.setFocusableInTouchMode(True)

def get_placeholder(self):
return str(self.native.getHint())

def set_placeholder(self, value):
# Android EditText's setHint() requires a Python string.
self.native.setHint(value if value is not None else "")
self.native.setHint(value)

def set_alignment(self, value):
self.set_textview_alignment(value, Gravity.CENTER_VERTICAL)

def set_value(self, value):
self.native.setText(value)

def set_on_change(self, handler):
if self._textChangedListener:
self.native.removeTextChangedListener(self._textChangedListener)
self._textChangedListener = TogaTextWatcher(self)
self.native.addTextChangedListener(self._textChangedListener)
def set_background_color(self, value):
# This causes any custom color to hide the bottom border line, but it's better
# than set_background_filter, which affects *only* the bottom border line.
self.set_background_simple(value)

def set_error(self, error_message):
self.interface.factory.not_implemented("TextInput.set_error()")
self.native.setError(error_message)

def clear_error(self):
self.interface.factory.not_implemented("TextInput.clear_error()")
self.native.setError(None)

def is_valid(self):
self.interface.factory.not_implemented("TextInput.is_valid()")
return self.native.getError() is None

def rehint(self):
self.interface.intrinsic.width = at_least(self.interface._MIN_WIDTH)
Expand All @@ -94,9 +131,3 @@ def rehint(self):
View__MeasureSpec.UNSPECIFIED, View__MeasureSpec.UNSPECIFIED
)
self.interface.intrinsic.height = self.native.getMeasuredHeight()

def set_on_gain_focus(self, handler):
self.interface.factory.not_implemented("TextInput.set_on_gain_focus()")

def set_on_lose_focus(self, handler):
self.interface.factory.not_implemented("TextInput.set_on_lose_focus()")
5 changes: 2 additions & 3 deletions android/tests_backend/widgets/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,5 @@ def alignment(self):
)
return toga_alignment(self.native.getGravity(), justification_mode)

@property
def vertical_alignment(self):
return toga_vertical_alignment(self.native.getGravity())
def assert_vertical_alignment(self, expected):
assert toga_vertical_alignment(self.native.getGravity()) == expected
31 changes: 2 additions & 29 deletions android/tests_backend/widgets/multilinetextinput.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,7 @@
from java import jclass
from .textinput import TextInputProbe

from .label import LabelProbe


class MultilineTextInputProbe(LabelProbe):
native_class = jclass("android.widget.EditText")

@property
def value(self):
return self.native.getHint() if self.placeholder_visible else self.text

@property
def placeholder_visible(self):
return not self.text

@property
def placeholder_hides_on_focus(self):
return False

@property
def readonly(self):
focusable = self.native.isFocusable()
focusable_in_touch_mode = self.native.isFocusableInTouchMode()
if focusable != focusable_in_touch_mode:
raise ValueError(f"invalid state: {focusable=}, {focusable_in_touch_mode=}")
return not focusable

class MultilineTextInputProbe(TextInputProbe):
@property
def document_height(self):
return self.native.getLayout().getHeight() / self.scale_factor
Expand All @@ -40,6 +16,3 @@ def vertical_scroll_position(self):

async def wait_for_scroll_completion(self):
pass

async def type_character(self, char):
self.native.append(char)
13 changes: 13 additions & 0 deletions android/tests_backend/widgets/passwordinput.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from toga.fonts import SYSTEM

from .textinput import TextInputProbe


class PasswordInputProbe(TextInputProbe):
# In password mode, the EditText defaults to monospace.
def assert_font_family(self, expected):
actual = self.font.family
if expected == SYSTEM:
assert actual == "monospace"
else:
assert actual == expected
Loading

0 comments on commit da29226

Please sign in to comment.