From 78e6704639b83bcc38ccf522780d0f6f2f2d4b89 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 25 Sep 2023 23:12:19 -0500 Subject: [PATCH] Add on-screen keyboard for feedback dialog --- wuttapos/controls/feedback.py | 153 ++++++++++++++++++++++++++++++++++ wuttapos/views/pos.py | 149 +++++++-------------------------- 2 files changed, 183 insertions(+), 119 deletions(-) create mode 100644 wuttapos/controls/feedback.py diff --git a/wuttapos/controls/feedback.py b/wuttapos/controls/feedback.py new file mode 100644 index 0000000..07c8852 --- /dev/null +++ b/wuttapos/controls/feedback.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# WuttaPOS -- Pythonic Point of Sale System +# Copyright © 2023 Lance Edgar +# +# This file is part of WuttaPOS. +# +# WuttaPOS is free software: you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later +# version. +# +# WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# WuttaPOS. If not, see . +# +################################################################################ +""" +WuttaPOS - feedback control +""" + +import flet as ft + +from .base import WuttaControl +from .keyboard import WuttaKeyboard + + +class WuttaFeedback(WuttaControl): + + default_font_size = 20 + default_button_height = 60 + + def __init__(self, *args, **kwargs): + self.on_send = kwargs.pop('on_send', None) + self.on_cancel = kwargs.pop('on_cancel', None) + super().__init__(*args, **kwargs) + + def build(self): + + self.button = ft.Container(content=ft.Text("Feedback", size=self.default_font_size, + weight=ft.FontWeight.BOLD), + height=self.default_button_height, + width=self.default_button_height * 3, + on_click=self.initial_click, + alignment=ft.alignment.center, + border=ft.border.all(1, 'black'), + border_radius=ft.border_radius.all(5), + bgcolor='blue') + + return self.button + + def initial_click(self, e): + + self.message = ft.TextField(label="Message", + multiline=True, min_lines=5, + autofocus=True) + + self.dlg = ft.AlertDialog( + modal=True, + title=ft.Text("User Feedback"), + content=ft.Container( + content=ft.Column( + [ + ft.Text("Questions, suggestions, comments, complaints, etc. " + "are welcome and may be submitted below. "), + ft.Divider(), + self.message, + ft.Divider(), + WuttaKeyboard(self.config, on_keypress=self.keypress), + ], + expand=True, + ), + height=800, + ), + actions=[ + ft.Row( + [ + ft.Container(content=ft.Text("Send Message", + size=self.default_font_size, + color='black', + weight=ft.FontWeight.BOLD), + height=self.default_button_height, + width=self.default_button_height * 3, + alignment=ft.alignment.center, + bgcolor='blue', + border=ft.border.all(1, 'black'), + border_radius=ft.border_radius.all(5), + on_click=self.send_feedback), + ft.Container(content=ft.Text("Cancel", + size=self.default_font_size, + weight=ft.FontWeight.BOLD), + height=self.default_button_height, + width=self.default_button_height * 2.5, + alignment=ft.alignment.center, + border=ft.border.all(1, 'black'), + border_radius=ft.border_radius.all(5), + on_click=self.cancel), + ], + alignment=ft.MainAxisAlignment.CENTER, + ), + ], + ) + + self.page.dialog = self.dlg + self.dlg.open = True + self.page.update() + + def keypress(self, key): + if key == '⏎': + self.message.value += '\n' + elif key == '⌫': + self.message.value = self.message.value[:-1] + else: + self.message.value += key + + self.message.focus() + self.update() + + def cancel(self, e): + self.dlg.open = False + self.page.update() + + if self.on_cancel: + self.on_cancel(e) + + def send_feedback(self, e): + if self.message.value: + + self.app.send_email('pos_feedback', data={ + 'user_name': self.page.session.get('user_display'), + 'message': self.message.value, + }) + + self.dlg.open = False + self.page.snack_bar = ft.SnackBar(ft.Text(f"MESSAGE WAS SENT", + color='black', + weight=ft.FontWeight.BOLD), + bgcolor='green', + duration=1500) + self.page.snack_bar.open = True + self.page.update() + + if self.on_send: + self.on_send() + + else: + self.message.focus() + self.page.update() diff --git a/wuttapos/views/pos.py b/wuttapos/views/pos.py index f38cdf2..2e9f8fc 100644 --- a/wuttapos/views/pos.py +++ b/wuttapos/views/pos.py @@ -32,6 +32,7 @@ import flet as ft from .base import WuttaView from wuttapos.controls.custlookup import WuttaCustomerLookup +from wuttapos.controls.feedback import WuttaFeedback from wuttapos.util import get_pos_batch_handler @@ -66,6 +67,22 @@ class POSView(WuttaView): def get_batch_handler(self): return get_pos_batch_handler(self.config) + def reset(self, e=None, update=True): + """ + This is a convenience method, meant only to clear the main + input and set focus to it. Will also update() the page + by default. + + The ``e`` arg is ignored and accepted only so this method may + be registered as an event handler, e.g. ``on_cancel``. + """ + self.set_quantity.data = None + self.set_quantity.value = None + self.main_input.value = '' + self.main_input.focus() + if update: + self.page.update() + def set_customer(self, customer, batch=None): session = self.app.get_session(customer) if not batch: @@ -96,13 +113,11 @@ class POSView(WuttaView): session.close() dlg.open = False - self.main_input.focus() - self.page.update() + self.reset() def cancel(e): dlg.open = False - self.main_input.focus() - self.page.update() + self.reset() dlg = ft.AlertDialog( modal=True, @@ -144,9 +159,7 @@ class POSView(WuttaView): def close(e): dlg.open = False - self.main_input.value = '' - self.main_input.focus() - self.page.update() + self.reset() font_size = self.default_font_size * 0.8 dlg = ft.AlertDialog( @@ -219,9 +232,7 @@ class POSView(WuttaView): def cancel(e): dlg.open = False - self.main_input.value = '' - self.main_input.focus() - self.page.update() + self.reset() font_size = self.default_font_size * 0.8 dlg = ft.AlertDialog( @@ -291,21 +302,17 @@ class POSView(WuttaView): self.informed_refresh() dlg.open = False - self.main_input.value = '' - self.main_input.focus() self.page.snack_bar = ft.SnackBar(ft.Text("CUSTOMER REMOVED", color='black', weight=ft.FontWeight.BOLD), bgcolor='yellow', duration=1500) self.page.snack_bar.open = True - self.page.update() + self.reset() def cancel(e): dlg.open = False - self.main_input.value = '' - self.main_input.focus() - self.page.update() + self.reset() font_size = self.default_font_size * 0.8 dlg = ft.AlertDialog( @@ -344,9 +351,7 @@ class POSView(WuttaView): if customer: self.set_customer(customer) - self.main_input.value = '' - self.main_input.focus() - self.page.update() + self.reset() else: # customer not found self.page.snack_bar = ft.SnackBar(ft.Text(f"CUSTOMER NOT FOUND: {entry}", @@ -355,6 +360,7 @@ class POSView(WuttaView): bgcolor='yellow', duration=1500) self.page.snack_bar.open = True + # TODO: should use reset() here? self.main_input.focus() self.page.update() @@ -403,91 +409,6 @@ class POSView(WuttaView): expand=1, ) - def feedback_click(e): - - message = ft.TextField(label="Message", - multiline=True, min_lines=5, - autofocus=True) - - def send_feedback(e): - - if message.value: - - self.app.send_email('pos_feedback', data={ - 'user_name': self.page.session.get('user_display'), - 'message': message.value, - }) - - dlg.open = False - - self.page.snack_bar = ft.SnackBar(ft.Text(f"MESSAGE WAS SENT", - color='black', - weight=ft.FontWeight.BOLD), - bgcolor='green', - duration=1500) - self.page.snack_bar.open = True - - self.main_input.focus() - - else: - message.focus() - - self.page.update() - - def cancel(e): - dlg.open = False - self.main_input.focus() - self.page.update() - - button_height = self.default_button_size * 0.8 - dlg = ft.AlertDialog( - modal=True, - title=ft.Text("User Feedback"), - content=ft.Container( - content=ft.Column( - [ - ft.Text("Questions, suggestions, comments, complaints, etc. " - "are welcome and may be submitted below. "), - ft.Divider(), - message, - ], - expand=True, - ), - height=500, - ), - actions=[ - ft.Row( - [ - ft.Container(content=ft.Text("Send Message", - size=self.default_font_size, - color='black', - weight=ft.FontWeight.BOLD), - height=button_height, - width=self.default_button_size * 3, - alignment=ft.alignment.center, - bgcolor='blue', - border=ft.border.all(1, 'black'), - border_radius=ft.border_radius.all(5), - on_click=send_feedback), - ft.Container(content=ft.Text("Cancel", - size=self.default_font_size, - weight=ft.FontWeight.BOLD), - height=button_height, - width=self.default_button_size * 2.5, - alignment=ft.alignment.center, - border=ft.border.all(1, 'black'), - border_radius=ft.border_radius.all(5), - on_click=cancel), - ], - alignment=ft.MainAxisAlignment.CENTER, - ), - ], - ) - - self.page.dialog = dlg - dlg.open = True - self.page.update() - def tenkey_click(e): value = e.control.content.value @@ -720,8 +641,7 @@ class POSView(WuttaView): ft.Row( [ - tenkey_button("Feedback", height=70, width=200, - bgcolor='blue', on_click=feedback_click), + WuttaFeedback(self.config, on_send=self.reset, on_cancel=self.reset), ft.Row( [ self.set_quantity, @@ -848,14 +768,11 @@ class POSView(WuttaView): session.close() self.clear_all() - self.main_input.value = '' - self.main_input.focus() - self.page.update() + self.reset() def cancel(e): dlg.open = False - self.main_input.focus() - self.page.update() + self.reset() session = self.app.make_session() batch = self.get_current_batch(session, create=False) @@ -924,8 +841,7 @@ class POSView(WuttaView): session.close() self.clear_all() - self.main_input.focus() - self.page.update() + self.reset() def clear_all(self): self.items.controls.clear() @@ -972,9 +888,4 @@ class POSView(WuttaView): session.commit() session.close() - - self.main_input.value = "" - self.main_input.focus() - self.set_quantity.data = None - self.set_quantity.value = None - self.page.update() + self.reset()