From faccfd8ca1fd4d0c7acd8a1f47093c97e4834c8b Mon Sep 17 00:00:00 2001 From: marxzuckerburg Date: Thu, 1 Oct 2020 11:36:54 +0100 Subject: [PATCH] fixed issue #44 --- comrad/app/main.py | 22 +- comrad/app/screens/login/login.kv | 239 ++++++++++++- comrad/app/screens/login/login.py | 554 +++++++++++++++++++++++++++++- comrad/backend/comrades.py | 2 +- 4 files changed, 804 insertions(+), 13 deletions(-) diff --git a/comrad/app/main.py b/comrad/app/main.py index 861b7b0..2f1656d 100644 --- a/comrad/app/main.py +++ b/comrad/app/main.py @@ -183,9 +183,8 @@ class MessagePopupCard(MDDialog2): # logger.info(str(self.msg_dialog)) - class TextInputPopupCard(MDDialog2): - def say(self,x): + def say(self,x=None): self.ok_to_continue=True self.response=self.field.text return self.response @@ -197,6 +196,14 @@ class TextInputPopupCard(MDDialog2): title=msg from comrad.app.screens.login.login import UsernameField,PasswordField,UsernameLayout,UsernameLabel + class TextInputPopupCardField(UsernameField): + def on_text_validate(self): + self.has_had_text = True + self._set_text_len_error() + + # custom + self.card.say() + self.layout=MDBoxLayout() self.layout.orientation='vertical' self.layout.cols=1 @@ -212,11 +219,18 @@ class TextInputPopupCard(MDDialog2): self.field_layout=UsernameLayout() - self.field = PasswordField() if password else UsernameField() + # self.field = PasswordField() if password else UsernameField() + self.field = TextInputPopupCardField() + self.field.card=self + self.field.password=True self.field.line_color_focus=rgb(*COLOR_TEXT) - self.field.line_color_normal=rgb(*COLOR_TEXT,a=0.25) + self.field.line_color_normal=rgb(*COLOR_TEXT) self.field.font_name=FONT_PATH self.field.font_size='20sp' + self.field.pos_hint={'center_y':0.5} + + + self.field_label = UsernameLabel(text='password:' if password else input_name) self.field_label.font_name=FONT_PATH diff --git a/comrad/app/screens/login/login.kv b/comrad/app/screens/login/login.kv index fb4f0e3..f5a05c1 100644 --- a/comrad/app/screens/login/login.kv +++ b/comrad/app/screens/login/login.kv @@ -11,6 +11,7 @@ #:import COLOR_CARD main.COLOR_CARD #:import COLOR_CARD_BORDER main.COLOR_CARD_BORDER #:import COLOR_ACCENT main.COLOR_ACCENT +#:import images_path kivymd.images_path : id: loginbox @@ -69,6 +70,23 @@ +# : + id: username + text: "" + hint_text: "username" + required: True + write_tab: False + multiline: False + helper_text_mode: "on_error" + color_mode: 'custom' + line_color_focus: rgb(*COLOR_TEXT,a=0) + line_color_normal: rgb(*COLOR_TEXT,a=0) + current_hint_text_color: rgb(*COLOR_TEXT) + error_color:rgb(*COLOR_TEXT) + # pos_hint: {'center_x':0.5,'y':0.2} + size_hint:1,None + # font_size:'2dp' + : id: username text: "" @@ -78,14 +96,122 @@ multiline: False helper_text_mode: "on_error" color_mode: 'custom' - line_color_focus: rgb(*COLOR_TEXT) - line_color_normal: rgb(*COLOR_TEXT) + line_color_focus: rgb(*COLOR_TEXT,a=0) + line_color_normal: rgb(*COLOR_TEXT,a=0) current_hint_text_color: rgb(*COLOR_TEXT) error_color:rgb(*COLOR_TEXT) # pos_hint: {'center_x':0.5,'y':0.2} - size_hint:0.666,None + size_hint:1,None # font_size:'2dp' + canvas.before: + Clear + # Disabled line. + Color: + rgba: self.line_color_normal if root.mode == "line" else (0, 0, 0, 0) + # rgba: (0,0,0,0) #self.line_color_normal if root.mode == "line" else (0, 0, 0, 0) + Line: + points: self.x, self.y + dp(16), self.x + self.width, self.y + dp(16) + width: 1 + dash_length: dp(3) + dash_offset: 2 if self.disabled else 0 + # Active line. + Color: + rgba: (0,0,0,0) + # rgba: self.line_color_normal if root.mode == "line" else (0, 0, 0, 0) + Line: + points: self.x, self.y + dp(16), self.x + self.width, self.y + dp(16) + width: 1 + dash_length: dp(3) + dash_offset: 2 if self.disabled else 0 + # Color: + # rgba: self._current_line_color if root.mode in ("line", "fill") and root.active_line else (0, 0, 0, 0) + # Rectangle: + # size: self._line_width, dp(2) + # pos: self.center_x - (self._line_width / 2), self.y + (dp(16) if root.mode != "fill" else 0) + # Helper text. + Color: + rgba: self._current_error_color + Rectangle: + texture: self._msg_lbl.texture + size: + self._msg_lbl.texture_size[0] - (dp(3) if root.mode in ("fill", "rectangle") else 0), \ + self._msg_lbl.texture_size[1] - (dp(3) if root.mode in ("fill", "rectangle") else 0) + pos: self.x + (dp(8) if root.mode == "fill" else 0), self.y + (dp(3) if root.mode in ("fill", "rectangle") else 0) + # Texture of right Icon. + Color: + rgba: self.icon_right_color + Rectangle: + texture: self._lbl_icon_right.texture + size: self._lbl_icon_right.texture_size if self.icon_right else (0, 0) + pos: + (self.width + self.x) - (self._lbl_icon_right.texture_size[1]) - dp(8), \ + self.center[1] - self._lbl_icon_right.texture_size[1] / 2 + (dp(8) if root.mode != "fill" else 0) \ + if root.mode != "rectangle" else \ + self.center[1] - self._lbl_icon_right.texture_size[1] / 2 - dp(4) + Color: + rgba: self._current_right_lbl_color + Rectangle: + texture: self._right_msg_lbl.texture + size: self._right_msg_lbl.texture_size + pos: self.x + self.width - self._right_msg_lbl.texture_size[0] - dp(16), self.y + 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 + # Hint text. + Color: + rgba: self._current_hint_text_color if not self.current_hint_text_color else self.current_hint_text_color + Rectangle: + texture: self._hint_lbl.texture + size: self._hint_lbl.texture_size + pos: self.x + (dp(8) if root.mode == "fill" else 0), self.y + self.height - 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) + # "rectangle" mode + Color: + rgba: self._current_line_color + Line: + width: dp(1) if root.mode == "rectangle" else dp(0.00001) + points: + ( + self.x + root._line_blank_space_right_point, self.top - self._hint_lbl.texture_size[1] // 2, + self.right + dp(12), self.top - self._hint_lbl.texture_size[1] // 2, + self.right + dp(12), self.y, + self.x - dp(12), self.y, + self.x - dp(12), self.top - self._hint_lbl.texture_size[1] // 2, + self.x + root._line_blank_space_left_point, self.top - self._hint_lbl.texture_size[1] // 2 + ) + # "fill" mode. + canvas.after: + Color: + rgba: root.fill_color if root.mode == "fill" else (0, 0, 0, 0) + RoundedRectangle: + pos: self.x, self.y + size: self.width, self.height + dp(8) + radius: (10, 10, 0, 0, 0) + font_name: "Roboto" if not root.font_name else root.font_name + foreground_color: app.theme_cls.text_color + font_size: "16sp" + bold: False + padding: + 0 if root.mode != "fill" else "8dp", \ + "16dp" if root.mode != "fill" else "24dp", \ + 0 if root.mode != "fill" and not root.icon_right else ("14dp" if not root.icon_right else self._lbl_icon_right.texture_size[1] + dp(20)), \ + "16dp" if root.mode == "fill" else "10dp" + multiline: False + size_hint_y: None + height: self.minimum_height + (dp(8) if root.mode != "fill" else 0) + + + + : @@ -182,4 +308,109 @@ # LoginButtonLayout: # LoginButton: # RegisterButton: - # LoginStatus: \ No newline at end of file + # LoginStatus: + + + + + + + + + + + + + + + + + size_hint_x: None + width: self.texture_size[0] + shorten: True + shorten_from: "right" + + on_focus: + root.anim_rect((root.x, root.y, root.right, root.y, root.right, \ + root.top, root.x, root.top, root.x, root.y), 1) if root.focus \ + else root.anim_rect((root.x - dp(60), root.y - dp(60), \ + root.right + dp(60), root.y - dp(60), + root.right + dp(60), root.top + dp(60), \ + root.x - dp(60), root.top + dp(60), \ + root.x - dp(60), root.y - dp(60)), 0) + canvas.after: + Color: + rgba: root._primary_color + Line: + width: dp(1.5) + points: + ( + self.x - dp(60), self.y - dp(60), + self.right + dp(60), self.y - dp(60), + self.right + dp(60), self.top + dp(60), + self.x - dp(60), self.top + dp(60), + self.x - dp(60), self.y - dp(60) + ) +: + multiline: False + size_hint: 1, None + height: self.line_height + dp(10) + background_active: f'{images_path}transparent.png' + background_normal: f'{images_path}transparent.png' + padding: + self._lbl_icon_left.texture_size[1] + dp(10) if self.icon_left else dp(15), \ + (self.height / 2) - (self.line_height / 2), \ + self._lbl_icon_right.texture_size[1] + dp(20) if self.icon_right else dp(15), \ + 0 + canvas.before: + Color: + rgba: self.normal_color if not self.focus else self._color_active + Ellipse: + angle_start: 180 + angle_end: 360 + pos: self.pos[0] - self.size[1] / 2, self.pos[1] + size: self.size[1], self.size[1] + Ellipse: + angle_start: 360 + angle_end: 540 + pos: self.size[0] + self.pos[0] - self.size[1]/2.0, self.pos[1] + size: self.size[1], self.size[1] + Rectangle: + pos: self.pos + size: self.size + Color: + rgba: self.line_color + Line: + points: self.pos[0] , self.pos[1], self.pos[0] + self.size[0], self.pos[1] + Line: + points: self.pos[0], self.pos[1] + self.size[1], self.pos[0] + self.size[0], self.pos[1] + self.size[1] + Line: + ellipse: self.pos[0] - self.size[1] / 2, self.pos[1], self.size[1], self.size[1], 180, 360 + Line: + ellipse: self.size[0] + self.pos[0] - self.size[1] / 2.0, self.pos[1], self.size[1], self.size[1], 360, 540 + # Texture of left Icon. + Color: + rgba: self.icon_left_color + Rectangle: + texture: self._lbl_icon_left.texture + size: + self._lbl_icon_left.texture_size if self.icon_left \ + else (0, 0) + pos: + self.x, \ + self.center[1] - self._lbl_icon_right.texture_size[1] / 2 + # Texture of right Icon. + Color: + rgba: self.icon_right_color + Rectangle: + texture: self._lbl_icon_right.texture + size: + self._lbl_icon_right.texture_size if self.icon_right \ + else (0, 0) + pos: + (self.width + self.x) - (self._lbl_icon_right.texture_size[1]), \ + self.center[1] - self._lbl_icon_right.texture_size[1] / 2 + Color: + rgba: + root.theme_cls.disabled_hint_text_color if not self.focus \ + else root.foreground_color \ No newline at end of file diff --git a/comrad/app/screens/login/login.py b/comrad/app/screens/login/login.py index befcd65..13ee3ed 100644 --- a/comrad/app/screens/login/login.py +++ b/comrad/app/screens/login/login.py @@ -15,9 +15,529 @@ from kivy.app import * import logging logger = logging.getLogger(__name__) +import sys + +from kivy.animation import Animation +from kivy.lang import Builder +from kivy.metrics import dp, sp +from kivy.properties import ( + BooleanProperty, + ListProperty, + NumericProperty, + ObjectProperty, + OptionProperty, + StringProperty, +) +from kivy.uix.label import Label +from kivy.uix.textinput import TextInput + +from kivymd.font_definitions import theme_font_styles +from kivymd.material_resources import DEVICE_TYPE +from kivymd.theming import ThemableBehavior +from kivymd.uix.label import MDIcon + + + + + + + + class LoginBoxLayout(MDBoxLayout): pass class LoginButtonLayout(MDBoxLayout): pass -class UsernameField(MDTextField): pass +# # class UsernameField(MDTextField): pass +# def __init__(self,*x,**y): +# super().__init__(*x,**y) +# self.focus=True +# def on_text_validate(self): +# reg_button=self.parent.parent.parent.register_button +# reg_button.enter() + + + +class UsernameField(ThemableBehavior, TextInput): + helper_text = StringProperty("This field is required") + """ + Text for ``helper_text`` mode. + :attr:`helper_text` is an :class:`~kivy.properties.StringProperty` + and defaults to `'This field is required'`. + """ + + helper_text_mode = OptionProperty( + "none", options=["none", "on_error", "persistent", "on_focus"] + ) + """ + Helper text mode. Available options are: `'on_error'`, `'persistent'`, + `'on_focus'`. + :attr:`helper_text_mode` is an :class:`~kivy.properties.OptionProperty` + and defaults to `'none'`. + """ + + max_text_length = NumericProperty(None) + """ + Maximum allowed value of characters in a text field. + :attr:`max_text_length` is an :class:`~kivy.properties.NumericProperty` + and defaults to `None`. + """ + + required = BooleanProperty(False) + """ + Required text. If True then the text field requires text. + :attr:`required` is an :class:`~kivy.properties.BooleanProperty` + and defaults to `False`. + """ + + color_mode = OptionProperty( + "primary", options=["primary", "accent", "custom"] + ) + """ + Color text mode. Available options are: `'primary'`, `'accent'`, + `'custom'`. + :attr:`color_mode` is an :class:`~kivy.properties.OptionProperty` + and defaults to `'primary'`. + """ + + mode = OptionProperty("line", options=["rectangle", "fill"]) + """ + Text field mode. Available options are: `'line'`, `'rectangle'`, `'fill'`. + :attr:`mode` is an :class:`~kivy.properties.OptionProperty` + and defaults to `'line'`. + """ + + line_color_normal = ListProperty() + """ + Line color normal in ``rgba`` format. + :attr:`line_color_normal` is an :class:`~kivy.properties.ListProperty` + and defaults to `[]`. + """ + + line_color_focus = ListProperty() + """ + Line color focus in ``rgba`` format. + :attr:`line_color_focus` is an :class:`~kivy.properties.ListProperty` + and defaults to `[]`. + """ + + error_color = ListProperty() + """ + Error color in ``rgba`` format for ``required = True``. + :attr:`error_color` is an :class:`~kivy.properties.ListProperty` + and defaults to `[]`. + """ + + fill_color = ListProperty((0, 0, 0, 0)) + """ + The background color of the fill in rgba format when the ``mode`` parameter + is "fill". + :attr:`fill_color` is an :class:`~kivy.properties.ListProperty` + and defaults to `(0, 0, 0, 0)`. + """ + + active_line = BooleanProperty(True) + """ + Show active line or not. + :attr:`active_line` is an :class:`~kivy.properties.BooleanProperty` + and defaults to `True`. + """ + + error = BooleanProperty(False) + """ + If True, then the text field goes into ``error`` mode. + :attr:`error` is an :class:`~kivy.properties.BooleanProperty` + and defaults to `False`. + """ + + current_hint_text_color = ListProperty() + """ + ``hint_text`` text color. + :attr:`current_hint_text_color` is an :class:`~kivy.properties.ListProperty` + and defaults to `[]`. + """ + + icon_right = StringProperty() + """Right icon. + :attr:`icon_right` is an :class:`~kivy.properties.StringProperty` + and defaults to `''`. + """ + + icon_right_color = ListProperty((0, 0, 0, 1)) + """Color of right icon in ``rgba`` format. + :attr:`icon_right_color` is an :class:`~kivy.properties.ListProperty` + and defaults to `(0, 0, 0, 1)`. + """ + + _text_len_error = BooleanProperty(False) + _hint_lbl_font_size = NumericProperty("16sp") + _line_blank_space_right_point = NumericProperty(0) + _line_blank_space_left_point = NumericProperty(0) + _hint_y = NumericProperty("38dp") + _line_width = NumericProperty(0) + _current_line_color = ListProperty((0, 0, 0, 0)) + _current_error_color = ListProperty((0, 0, 0, 0)) + _current_hint_text_color = ListProperty((0, 0, 0, 0)) + _current_right_lbl_color = ListProperty((0, 0, 0, 0)) + + _msg_lbl = None + _right_msg_lbl = None + _hint_lbl = None + _lbl_icon_right = None + + def __init__(self, **kwargs): + self.set_objects_labels() + super().__init__(**kwargs) + # Sets default colors. + self.line_color_normal = self.theme_cls.divider_color + self.line_color_focus = self.theme_cls.primary_color + self.error_color = self.theme_cls.error_color + self._current_hint_text_color = self.theme_cls.disabled_hint_text_color + self._current_line_color = self.theme_cls.primary_color + + self.bind( + helper_text=self._set_msg, + hint_text=self._set_hint, + _hint_lbl_font_size=self._hint_lbl.setter("font_size"), + helper_text_mode=self._set_message_mode, + max_text_length=self._set_max_text_length, + text=self.on_text, + ) + self.theme_cls.bind( + primary_color=self._update_primary_color, + theme_style=self._update_theme_style, + accent_color=self._update_accent_color, + ) + self.has_had_text = False + self.focus=True + + def set_objects_labels(self): + """Creates labels objects for the parameters + `helper_text`,`hint_text`, etc.""" + + # Label object for `helper_text` parameter. + self._msg_lbl = TextfieldLabel( + font_style="Caption", + halign="left", + valign="middle", + text=self.helper_text, + field=self, + ) + # Label object for `max_text_length` parameter. + self._right_msg_lbl = TextfieldLabel( + font_style="Caption", + halign="right", + valign="middle", + text="", + field=self, + ) + # Label object for `hint_text` parameter. + self._hint_lbl = TextfieldLabel( + font_style="Subtitle1", halign="left", valign="middle", field=self + ) + # MDIcon object for the icon on the right. + self._lbl_icon_right = MDIcon(theme_text_color="Custom") + + def on_icon_right(self, instance, value): + self._lbl_icon_right.icon = value + + def on_icon_right_color(self, instance, value): + self._lbl_icon_right.text_color = value + + def on_width(self, instance, width): + """Called when the application window is resized.""" + + if ( + any((self.focus, self.error, self._text_len_error)) + and instance is not None + ): + # Bottom line width when active focus. + self._line_width = width + self._msg_lbl.width = self.width + self._right_msg_lbl.width = self.width + + def on_focus(self, *args): + disabled_hint_text_color = self.theme_cls.disabled_hint_text_color + Animation.cancel_all( + self, "_line_width", "_hint_y", "_hint_lbl_font_size" + ) + self._set_text_len_error() + + if self.focus: + self._line_blank_space_right_point = ( + self._get_line_blank_space_right_point() + ) + _fill_color = self.fill_color + _fill_color[3] = self.fill_color[3] - 0.1 + if not self._get_has_error(): + Animation( + _line_blank_space_right_point=self._line_blank_space_right_point, + _line_blank_space_left_point=self._hint_lbl.x - dp(5), + _current_hint_text_color=self.line_color_focus, + fill_color=_fill_color, + duration=0.2, + t="out_quad", + ).start(self) + self.has_had_text = True + Animation.cancel_all( + self, "_line_width", "_hint_y", "_hint_lbl_font_size" + ) + if not self.text: + self._anim_lbl_font_size(dp(14), sp(12)) + Animation(_line_width=self.width, duration=0.2, t="out_quad").start( + self + ) + if self._get_has_error(): + self._anim_current_error_color(self.error_color) + if self.helper_text_mode == "on_error" and ( + self.error or self._text_len_error + ): + self._anim_current_error_color(self.error_color) + elif ( + self.helper_text_mode == "on_error" + and not self.error + and not self._text_len_error + ): + self._anim_current_error_color((0, 0, 0, 0)) + elif self.helper_text_mode in ("persistent", "on_focus"): + self._anim_current_error_color(disabled_hint_text_color) + else: + self._anim_current_right_lbl_color(disabled_hint_text_color) + Animation( + duration=0.2, _current_hint_text_color=self.line_color_focus + ).start(self) + if self.helper_text_mode == "on_error": + self._anim_current_error_color((0, 0, 0, 0)) + if self.helper_text_mode in ("persistent", "on_focus"): + self._anim_current_error_color(disabled_hint_text_color) + else: + _fill_color = self.fill_color + _fill_color[3] = self.fill_color[3] + 0.1 + Animation(fill_color=_fill_color, duration=0.2, t="out_quad").start( + self + ) + if not self.text: + self._anim_lbl_font_size(dp(38), sp(16)) + Animation( + _line_blank_space_right_point=0, + _line_blank_space_left_point=0, + duration=0.2, + t="out_quad", + ).start(self) + if self._get_has_error(): + self._anim_get_has_error_color(self.error_color) + if self.helper_text_mode == "on_error" and ( + self.error or self._text_len_error + ): + self._anim_current_error_color(self.error_color) + elif ( + self.helper_text_mode == "on_error" + and not self.error + and not self._text_len_error + ): + self._anim_current_error_color((0, 0, 0, 0)) + elif self.helper_text_mode == "persistent": + self._anim_current_error_color(disabled_hint_text_color) + elif self.helper_text_mode == "on_focus": + self._anim_current_error_color((0, 0, 0, 0)) + else: + Animation(duration=0.2, color=(1, 1, 1, 1)).start( + self._hint_lbl + ) + self._anim_get_has_error_color() + if self.helper_text_mode == "on_error": + self._anim_current_error_color((0, 0, 0, 0)) + elif self.helper_text_mode == "persistent": + self._anim_current_error_color(disabled_hint_text_color) + elif self.helper_text_mode == "on_focus": + self._anim_current_error_color((0, 0, 0, 0)) + Animation(_line_width=0, duration=0.2, t="out_quad").start(self) + + def on_text(self, instance, text): + if len(text) > 0: + self.has_had_text = True + if self.max_text_length is not None: + self._right_msg_lbl.text = f"{len(text)}/{self.max_text_length}" + self._set_text_len_error() + if self.error or self._text_len_error: + if self.focus: + self._anim_current_line_color(self.error_color) + if self.helper_text_mode == "on_error" and ( + self.error or self._text_len_error + ): + self._anim_current_error_color(self.error_color) + if self._text_len_error: + self._anim_current_right_lbl_color(self.error_color) + else: + if self.focus: + self._anim_current_right_lbl_color( + self.theme_cls.disabled_hint_text_color + ) + self._anim_current_line_color(self.line_color_focus) + if self.helper_text_mode == "on_error": + self._anim_current_error_color((0, 0, 0, 0)) + if len(self.text) != 0 and not self.focus: + self._hint_y = dp(14) + self._hint_lbl_font_size = sp(12) + + def on_text_validate(self): + self.has_had_text = True + self._set_text_len_error() + + # custom + reg_button=self.parent.parent.parent.register_button + reg_button.enter() + + def on_color_mode(self, instance, mode): + if mode == "primary": + self._update_primary_color() + elif mode == "accent": + self._update_accent_color() + elif mode == "custom": + self._update_colors(self.line_color_focus) + + def on_line_color_focus(self, *args): + if self.color_mode == "custom": + self._update_colors(self.line_color_focus) + + def on__hint_text(self, instance, value): + pass + + def _anim_get_has_error_color(self, color=None): + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_get_has_error.png + if not color: + line_color = self.line_color_focus + hint_text_color = self.theme_cls.disabled_hint_text_color + right_lbl_color = (0, 0, 0, 0) + else: + line_color = color + hint_text_color = color + right_lbl_color = color + Animation( + duration=0.2, + _current_line_color=line_color, + _current_hint_text_color=hint_text_color, + _current_right_lbl_color=right_lbl_color, + ).start(self) + + def _anim_current_line_color(self, color): + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_anim_current_line_color.gif + Animation( + duration=0.2, + _current_hint_text_color=color, + _current_line_color=color, + ).start(self) + + def _anim_lbl_font_size(self, hint_y, font_size): + Animation( + _hint_y=hint_y, + _hint_lbl_font_size=font_size, + duration=0.2, + t="out_quad", + ).start(self) + + def _anim_current_right_lbl_color(self, color, duration=0.2): + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_anim_current_right_lbl_color.png + Animation(duration=duration, _current_right_lbl_color=color).start(self) + + def _anim_current_error_color(self, color, duration=0.2): + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_anim_current_error_color_to_disabled_color.gif + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_anim_current_error_color_to_fade.gif + Animation(duration=duration, _current_error_color=color).start(self) + + def _update_colors(self, color): + self.line_color_focus = color + if not self.error and not self._text_len_error: + self._current_line_color = color + if self.focus: + self._current_line_color = color + + def _update_accent_color(self, *args): + if self.color_mode == "accent": + self._update_colors(self.theme_cls.accent_color) + + def _update_primary_color(self, *args): + if self.color_mode == "primary": + self._update_colors(self.theme_cls.primary_color) + + def _update_theme_style(self, *args): + self.line_color_normal = self.theme_cls.divider_color + if not any([self.error, self._text_len_error]): + if not self.focus: + self._current_hint_text_color = ( + self.theme_cls.disabled_hint_text_color + ) + self._current_right_lbl_color = ( + self.theme_cls.disabled_hint_text_color + ) + if self.helper_text_mode == "persistent": + self._current_error_color = ( + self.theme_cls.disabled_hint_text_color + ) + + def _get_has_error(self): + if self.error or all( + [ + self.max_text_length is not None + and len(self.text) > self.max_text_length + ] + ): + has_error = True + else: + if all((self.required, len(self.text) == 0, self.has_had_text)): + has_error = True + else: + has_error = False + return has_error + + def _get_line_blank_space_right_point(self): + # https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/_line_blank_space_right_point.png + return ( + self._hint_lbl.texture_size[0] + - self._hint_lbl.texture_size[0] / 100 * dp(18) + if DEVICE_TYPE == "desktop" + else dp(10) + ) + + def _get_max_text_length(self): + """Returns the maximum number of characters that can be entered in a + text field.""" + + return ( + sys.maxsize + if self.max_text_length is None + else self.max_text_length + ) + + def _set_text_len_error(self): + if len(self.text) > self._get_max_text_length() or all( + (self.required, len(self.text) == 0, self.has_had_text) + ): + self._text_len_error = True + else: + self._text_len_error = False + + def _set_hint(self, instance, text): + self._hint_lbl.text = text + + def _set_msg(self, instance, text): + self._msg_lbl.text = text + self.helper_text = text + + def _set_message_mode(self, instance, text): + self.helper_text_mode = text + if self.helper_text_mode == "persistent": + self._anim_current_error_color( + self.theme_cls.disabled_hint_text_color, 0.1 + ) + + def _set_max_text_length(self, instance, length): + self.max_text_length = length + self._right_msg_lbl.text = f"{len(self.text)}/{length}" + + def _refresh_hint_text(self): + pass + + + + class PasswordField(MDTextField): pass class LoginButton(MDRectangleFlatButton): pass class RegisterButton(MDRectangleFlatButton,Logger): @@ -74,8 +594,9 @@ class LoginScreen(BaseScreen): self.username_field = UsernameField() self.username_field.line_color_focus=rgb(*COLOR_TEXT) - self.username_field.line_color_normal=rgb(*COLOR_TEXT,a=0.25) + self.username_field.line_color_normal=rgb(*COLOR_TEXT) self.username_field.font_name='assets/font.otf' + self.username_field.width='10dp' self.layout_username.add_widget(self.label_username) self.layout_username.add_widget(self.username_field) @@ -125,7 +646,7 @@ class LoginScreen(BaseScreen): self.register_button.text='enter' self.username_field.font_size='24sp' self.label_username.padding_x=(10,20) - self.username_field.padding_x=(20,10) + # self.username_field.padding_x=(20,10) # self.username_field.padding_y=(25,0) self.username_field.pos_hint={'center_y':0.5} self.label_username.halign='right' @@ -426,4 +947,29 @@ class LoginScreen(BaseScreen): return resp_msg_d - \ No newline at end of file + + + + + + + + + + + + + + + + + + +class TextfieldLabel(ThemableBehavior, Label): + font_style = OptionProperty("Body1", options=theme_font_styles) + # + field = ObjectProperty() + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.font_size = sp(self.theme_cls.font_styles[self.font_style][1]) diff --git a/comrad/backend/comrades.py b/comrad/backend/comrades.py index d3f40d0..8528e86 100644 --- a/comrad/backend/comrades.py +++ b/comrad/backend/comrades.py @@ -742,7 +742,7 @@ class ComradX(Caller): inbox = [x for x in inbox if not x in set(read)] posts=[] - for post_id in inbox: + for post_id in reversed(inbox): self.log('???',post_id,inbox_prefix) try: res_post = self.read_post(post_id)