Add Kivy message composer screen UI
This commit is contained in:
parent
3f561057be
commit
f6c7e50acf
|
@ -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)
|
||||
|
|
188
src/bitmessagekivy/baseclass/msg_composer.py
Normal file
188
src/bitmessagekivy/baseclass/msg_composer.py
Normal 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"]
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -218,6 +218,9 @@ MDNavigationLayout:
|
|||
id:id_scanscreen
|
||||
Payment:
|
||||
id:id_payment
|
||||
Create:
|
||||
id:id_create
|
||||
|
||||
MDNavigationDrawer:
|
||||
id: nav_drawer
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue
Block a user