216 lines
9.5 KiB
Python
216 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from kivy.lang import Builder
|
|
from kivy.uix.textinput import TextInput
|
|
from kivy.properties import ObjectProperty, NumericProperty, StringProperty, \
|
|
ListProperty, BooleanProperty
|
|
from kivy.metrics import sp, dp
|
|
from kivy.animation import Animation
|
|
from kivymd.label import MDLabel
|
|
from kivymd.theming import ThemableBehavior
|
|
from kivy.clock import Clock
|
|
|
|
Builder.load_string('''
|
|
<SingleLineTextField>:
|
|
canvas.before:
|
|
Clear
|
|
Color:
|
|
rgba: self.line_color_normal
|
|
Line:
|
|
id: "the_line"
|
|
points: self.x, self.y + dp(8), self.x + self.width, self.y + dp(8)
|
|
width: 1
|
|
dash_length: dp(3)
|
|
dash_offset: 2 if self.disabled else 0
|
|
Color:
|
|
rgba: self._current_line_color
|
|
Rectangle:
|
|
size: self._line_width, dp(2)
|
|
pos: self.center_x - (self._line_width / 2), self.y + dp(8)
|
|
Color:
|
|
rgba: self._current_error_color
|
|
Rectangle:
|
|
texture: self._msg_lbl.texture
|
|
size: self._msg_lbl.texture_size
|
|
pos: self.x, self.y - dp(8)
|
|
Color:
|
|
rgba: (self._current_line_color if self.focus and not self.cursor_blink \
|
|
else (0, 0, 0, 0))
|
|
Rectangle:
|
|
pos: [int(x) for x in self.cursor_pos]
|
|
size: 1, -self.line_height
|
|
Color:
|
|
#rgba: self._hint_txt_color if not self.text and not self.focus\
|
|
#else (self.line_color_focus if not self.text or self.focus\
|
|
#else self.line_color_normal)
|
|
rgba: self._current_hint_text_color
|
|
Rectangle:
|
|
texture: self._hint_lbl.texture
|
|
size: self._hint_lbl.texture_size
|
|
pos: self.x, self.y + self._hint_y
|
|
Color:
|
|
rgba: self.disabled_foreground_color if self.disabled else \
|
|
(self.hint_text_color if not self.text and not self.focus else \
|
|
self.foreground_color)
|
|
|
|
font_name: 'Roboto'
|
|
foreground_color: app.theme_cls.text_color
|
|
font_size: sp(16)
|
|
bold: False
|
|
padding: 0, dp(16), 0, dp(10)
|
|
multiline: False
|
|
size_hint_y: None
|
|
height: dp(48)
|
|
''')
|
|
|
|
|
|
class SingleLineTextField(ThemableBehavior, TextInput):
|
|
line_color_normal = ListProperty()
|
|
line_color_focus = ListProperty()
|
|
error_color = ListProperty()
|
|
error = BooleanProperty(False)
|
|
message = StringProperty("")
|
|
message_mode = StringProperty("none")
|
|
mode = message_mode
|
|
|
|
_hint_txt_color = ListProperty()
|
|
_hint_lbl = ObjectProperty()
|
|
_hint_lbl_font_size = NumericProperty(sp(16))
|
|
_hint_y = NumericProperty(dp(10))
|
|
_error_label = ObjectProperty()
|
|
_line_width = NumericProperty(0)
|
|
_hint_txt = StringProperty('')
|
|
_current_line_color = line_color_focus
|
|
_current_error_color = ListProperty([0.0, 0.0, 0.0, 0.0])
|
|
_current_hint_text_color = _hint_txt_color
|
|
|
|
def __init__(self, **kwargs):
|
|
Clock.schedule_interval(self._update_color, 5)
|
|
self._msg_lbl = MDLabel(font_style='Caption',
|
|
theme_text_color='Error',
|
|
halign='left',
|
|
valign='middle',
|
|
text=self.message)
|
|
|
|
self._hint_lbl = MDLabel(font_style='Subhead',
|
|
halign='left',
|
|
valign='middle')
|
|
super(SingleLineTextField, self).__init__(**kwargs)
|
|
self.line_color_normal = self.theme_cls.divider_color
|
|
self.line_color_focus = list(self.theme_cls.primary_color)
|
|
self.base_line_color_focus = list(self.theme_cls.primary_color)
|
|
self.error_color = self.theme_cls.error_color
|
|
|
|
self._hint_txt_color = self.theme_cls.disabled_hint_text_color
|
|
self.hint_text_color = (1, 1, 1, 0)
|
|
self.cursor_color = self.theme_cls.primary_color
|
|
self.bind(message=self._set_msg,
|
|
hint_text=self._set_hint,
|
|
_hint_lbl_font_size=self._hint_lbl.setter('font_size'),
|
|
message_mode=self._set_mode)
|
|
self.hint_anim_in = Animation(_hint_y=dp(34),
|
|
_hint_lbl_font_size=sp(12), duration=.2,
|
|
t='out_quad')
|
|
|
|
self.hint_anim_out = Animation(_hint_y=dp(10),
|
|
_hint_lbl_font_size=sp(16), duration=.2,
|
|
t='out_quad')
|
|
|
|
def _update_color(self, *args):
|
|
self.line_color_normal = self.theme_cls.divider_color
|
|
self.base_line_color_focus = list(self.theme_cls.primary_color)
|
|
if not self.focus and not self.error:
|
|
self.line_color_focus = self.theme_cls.primary_color
|
|
Animation(duration=.2, _current_hint_text_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
if self.mode == "persistent":
|
|
Animation(duration=.1, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
if self.focus and not self.error:
|
|
self.cursor_color = self.theme_cls.primary_color
|
|
|
|
def on_hint_text_color(self, instance, color):
|
|
self._hint_txt_color = self.theme_cls.disabled_hint_text_color
|
|
self.hint_text_color = (1, 1, 1, 0)
|
|
|
|
def on_width(self, instance, width):
|
|
if self.focus and instance is not None or self.error and instance is not None:
|
|
self._line_width = width
|
|
self.anim = Animation(_line_width=width, duration=.2, t='out_quad')
|
|
self._msg_lbl.width = self.width
|
|
self._hint_lbl.width = self.width
|
|
|
|
def on_pos(self, *args):
|
|
self.hint_anim_in = Animation(_hint_y=dp(34),
|
|
_hint_lbl_font_size=sp(12), duration=.2,
|
|
t='out_quad')
|
|
self.hint_anim_out = Animation(_hint_y=dp(10),
|
|
_hint_lbl_font_size=sp(16), duration=.2,
|
|
t='out_quad')
|
|
|
|
def on_focus(self, *args):
|
|
if self.focus:
|
|
Animation.cancel_all(self, '_line_width', '_hint_y',
|
|
'_hint_lbl_font_size')
|
|
if len(self.text) == 0:
|
|
self.hint_anim_in.start(self)
|
|
if self.error:
|
|
Animation(duration=.2, _current_hint_text_color=self.error_color).start(self)
|
|
if self.mode == "on_error":
|
|
Animation(duration=.2, _current_error_color=self.error_color).start(self)
|
|
elif self.mode == "persistent":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
elif self.mode == "on_focus":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
else:
|
|
pass
|
|
elif not self.error:
|
|
self.on_width(None, self.width)
|
|
self.anim.start(self)
|
|
Animation(duration=.2, _current_hint_text_color=self.line_color_focus).start(self)
|
|
if self.mode == "on_error":
|
|
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
if self.mode == "persistent":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
elif self.mode == "on_focus":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
else:
|
|
pass
|
|
else:
|
|
Animation.cancel_all(self, '_line_width', '_hint_y',
|
|
'_hint_lbl_font_size')
|
|
if len(self.text) == 0:
|
|
self.hint_anim_out.start(self)
|
|
if not self.error:
|
|
self.line_color_focus = self.base_line_color_focus
|
|
Animation(duration=.2, _current_line_color=self.line_color_focus,
|
|
_current_hint_text_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
if self.mode == "on_error":
|
|
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
elif self.mode == "persistent":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
elif self.mode == "on_focus":
|
|
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
|
|
self.on_width(None, 0)
|
|
self.anim.start(self)
|
|
elif self.error:
|
|
Animation(duration=.2, _current_line_color=self.error_color,
|
|
_current_hint_text_color=self.error_color).start(self)
|
|
if self.mode == "on_error":
|
|
Animation(duration=.2, _current_error_color=self.error_color).start(self)
|
|
elif self.mode == "persistent":
|
|
Animation(duration=.2, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|
|
elif self.mode == "on_focus":
|
|
Animation(duration=.2, _current_error_color=(0, 0, 0, 0)).start(self)
|
|
|
|
def _set_hint(self, instance, text):
|
|
self._hint_lbl.text = text
|
|
|
|
def _set_msg(self, instance, text):
|
|
self._msg_lbl.text = text
|
|
self.message = text
|
|
|
|
def _set_mode(self, instance, text):
|
|
self.mode = text
|
|
if self.mode == "persistent":
|
|
Animation(duration=.1, _current_error_color=self.theme_cls.disabled_hint_text_color).start(self)
|