Add Kivy message composer screen UI

This commit is contained in:
shekhar-cis 2022-10-03 13:26:21 +05:30
parent 3f561057be
commit f6c7e50acf
Signed by untrusted user: shekhar-cis
GPG Key ID: F4F00AB04E83F9A7
5 changed files with 226 additions and 22 deletions

View File

@ -1,4 +1,4 @@
# pylint: disable=no-name-in-module, attribute-defined-outside-init, import-error
# pylint: disable=no-name-in-module, attribute-defined-outside-init, import-error, unused-argument
"""
All Common widgets of kivy are managed here.
"""
@ -24,6 +24,8 @@ from kivymd.uix.label import MDLabel
from kivymd.toast import kivytoast
from kivymd.uix.card import MDCardSwipe
from kivymd.uix.chip import MDChip
from kivymd.uix.dialog import MDDialog
from kivymd.uix.button import MDFlatButton
from pybitmessage.bitmessagekivy.get_platform import platform
from pybitmessage.bmconfigparser import config
@ -208,3 +210,27 @@ def msg_content_length(body, subject, max_length=50):
else:
subject = ((subject + ',' + body)[0:50] + continue_str).replace('\t', '').replace(' ', '')
return subject
def composer_common_dialog(alert_msg):
"""Common alert popup for message composer"""
is_android_width = .8
other_platform_width = .55
dialog_height = .25
width = is_android_width if platform == 'android' else other_platform_width
dialog_box = MDDialog(
text=alert_msg,
size_hint=(width, dialog_height),
buttons=[
MDFlatButton(
text="Ok", on_release=lambda x: callback_for_menu_items("Ok")
),
],
)
dialog_box.open()
def callback_for_menu_items(text_item, *arg):
"""Callback of alert box"""
dialog_box.dismiss()
toast(text_item)

View File

@ -0,0 +1,188 @@
# pylint: disable=unused-argument, consider-using-f-string, too-many-ancestors
# pylint: disable=no-member, no-name-in-module, too-few-public-methods, no-name-in-module
"""
Message composer screen UI
"""
import logging
from kivy.app import App
from kivy.properties import (
BooleanProperty,
ListProperty,
NumericProperty,
ObjectProperty,
)
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.screenmanager import Screen
from kivymd.uix.textfield import MDTextField
from pybitmessage import state
from pybitmessage.bitmessagekivy.get_platform import platform
from pybitmessage.bitmessagekivy.baseclass.common import (
toast, kivy_state_variables, composer_common_dialog
)
logger = logging.getLogger('default')
class Create(Screen):
"""Creates Screen class for kivy Ui"""
def __init__(self, **kwargs):
"""Getting Labels and address from addressbook"""
super(Create, self).__init__(**kwargs)
self.kivy_running_app = App.get_running_app()
self.kivy_state = kivy_state_variables()
self.dropdown_widget = DropDownWidget()
self.dropdown_widget.ids.txt_input.starting_no = 2
self.add_widget(self.dropdown_widget)
self.children[0].ids.id_scroll.bind(scroll_y=self.check_scroll_y)
def check_scroll_y(self, instance, somethingelse): # pylint: disable=unused-argument
"""show data on scroll down"""
if self.children[1].ids.btn.is_open:
self.children[1].ids.btn.is_open = False
class RV(RecycleView):
"""Recycling View class for kivy Ui"""
def __init__(self, **kwargs):
"""Recycling Method"""
super(RV, self).__init__(**kwargs)
class SelectableRecycleBoxLayout(
FocusBehavior, LayoutSelectionBehavior, RecycleBoxLayout
):
"""Adds selection and focus behaviour to the view"""
# pylint: disable = duplicate-bases
class DropDownWidget(BoxLayout):
"""DropDownWidget class for kivy Ui"""
# pylint: disable=too-many-statements
txt_input = ObjectProperty()
rv = ObjectProperty()
def __init__(self, **kwargs):
super(DropDownWidget, self).__init__(**kwargs)
self.kivy_running_app = App.get_running_app()
self.kivy_state = kivy_state_variables()
@staticmethod
def callback_for_msgsend(dt=0): # pylint: disable=unused-argument
"""Callback method for messagesend"""
state.kivyapp.root.ids.id_create.children[0].active = False
state.in_sent_method = True
state.kivyapp.back_press()
toast("sent")
def reset_composer(self):
"""Method will reset composer"""
self.ids.ti.text = ""
self.ids.btn.text = "Select"
self.ids.txt_input.text = ""
self.ids.subject.text = ""
self.ids.body.text = ""
toast("Reset message")
def auto_fill_fromaddr(self):
"""Fill the text automatically From Address"""
self.ids.ti.text = self.ids.btn.text
self.ids.ti.focus = True
def is_camara_attached(self):
"""Checks the camera availability in device"""
self.parent.parent.parent.ids.id_scanscreen.check_camera()
is_available = self.parent.parent.parent.ids.id_scanscreen.camera_available
return is_available
@staticmethod
def camera_alert():
"""Show camera availability alert message"""
feature_unavailable = 'Currently this feature is not available!'
cam_not_available = 'Camera is not available!'
alert_text = feature_unavailable if platform == 'android' else cam_not_available
composer_common_dialog(alert_text)
class MyTextInput(MDTextField):
"""MyTextInput class for kivy Ui"""
txt_input = ObjectProperty()
flt_list = ObjectProperty()
word_list = ListProperty()
starting_no = NumericProperty(3)
suggestion_text = ''
def __init__(self, **kwargs):
"""Getting Text Input."""
super(MyTextInput, self).__init__(**kwargs)
self.__lineBreak__ = 0
def on_text(self, instance, value): # pylint: disable=unused-argument
"""Find all the occurrence of the word"""
self.parent.parent.parent.parent.parent.ids.rv.data = []
max_recipient_len = 10
box_height = 250
box_max_height = 400
matches = [self.word_list[i] for i in range(
len(self.word_list)) if self.word_list[
i][:self.starting_no] == value[:self.starting_no]]
display_data = []
for i in matches:
display_data.append({'text': i})
self.parent.parent.parent.parent.parent.ids.rv.data = display_data
if len(matches) <= max_recipient_len:
self.parent.height = (box_height + (len(matches) * 20))
else:
self.parent.height = box_max_height
def keyboard_on_key_down(self, window, keycode, text, modifiers):
"""Keyboard on key Down"""
if self.suggestion_text and keycode[1] == 'tab' and modifiers is None:
self.insert_text(self.suggestion_text + ' ')
return True
return super(MyTextInput, self).keyboard_on_key_down(
window, keycode, text, modifiers)
class SelectableLabel(RecycleDataViewBehavior, Label):
"""Add selection support to the Label"""
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
"""Catch and handle the view changes"""
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(rv, index, data)
def on_touch_down(self, touch): # pylint: disable=inconsistent-return-statements
"""Add selection on touch down"""
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
"""Respond to the selection of items in the view"""
self.selected = is_selected
if is_selected:
logger.debug("selection changed to %s", rv.data[index])
rv.parent.txt_input.text = rv.parent.txt_input.text.replace(
rv.parent.txt_input.text, rv.data[index]["text"]
)

View File

@ -23,7 +23,6 @@
font_size: '15sp'
multiline: False
required: True
# height: self.parent.height/2
height: 100
current_hint_text_color: 0,0,0,0.5
helper_text_mode: "on_error"
@ -38,11 +37,8 @@
IdentitySpinner:
id: btn
background_color: app.theme_cls.primary_dark
values: app.variable_1
on_text: root.auto_fill_fromaddr() if self.text != 'Select' else ''
values: app.identity_list
option_cls: Factory.get("ComposerSpinnerOption")
#background_color: color_button if self.state == 'normal' else color_button_pressed
#background_down: 'atlas://data/images/defaulttheme/spinner'
background_normal: ''
background_color: app.theme_cls.primary_color
color: color_font
@ -62,7 +58,6 @@
size_hint_y: None
font_size: '15sp'
color: color_font
# height: self.parent.height/2
current_hint_text_color: 0,0,0,0.5
height: 100
hint_text: app.tr._('Type or Scan QR code for recipients address')
@ -79,7 +74,7 @@
if root.is_camara_attached(): app.set_screen('scanscreen')
else: root.camera_alert()
on_press:
app.root.ids.is_scanscreen.get_screen('composer')
app.root.ids.id_scanscreen.get_screen('composer')
MyMDTextField:
id: subject
@ -98,17 +93,6 @@
Color:
rgba: (0,0,0,1)
# MyMDTextField:
# id: body
# multiline: True
# hint_text: 'body'
# size_hint_y: None
# font_size: '15sp'
# required: True
# helper_text_mode: "on_error"
# canvas.before:
# Color:
# rgba: (0,0,0,1)
ScrollView:
id: scrlv
MDTextField:
@ -171,8 +155,6 @@
<ComposerSpinnerOption@SpinnerOption>:
font_size: '13.5sp'
#background_color: color_button if self.state == 'down' else color_button_pressed
#background_down: 'atlas://data/images/defaulttheme/button'
background_normal: 'atlas://data/images/defaulttheme/textinput_active'
background_color: app.theme_cls.primary_color
color: color_font
color: color_font

View File

@ -218,6 +218,9 @@ MDNavigationLayout:
id:id_scanscreen
Payment:
id:id_payment
Create:
id:id_create
MDNavigationDrawer:
id: nav_drawer

View File

@ -16,5 +16,10 @@
"kv_string": "payment",
"name_screen": "payment",
"Import": "from pybitmessage.bitmessagekivy.baseclass.payment import Payment"
},
"Create": {
"kv_string": "msg_composer",
"name_screen": "create",
"Import": "from pybitmessage.bitmessagekivy.baseclass.msg_composer import Create"
}
}