feat: begin abstraction for more flexible button menus

plenty more to do here yet, but i'm mostly happy with these patterns.

i don't love the `pos` reference being passed from View down to
Button, since any intermediary controls may not (yet?) need the ref
but must maintain it so buttons can run pos.cmd()

also the `entry` param for cmd() may not be that useful since we can
pass arbitrary kwargs?
This commit is contained in:
Lance Edgar 2024-07-06 23:32:51 -05:00
parent 34390e4320
commit 4eb9442fce
17 changed files with 1052 additions and 717 deletions

View file

@ -41,7 +41,8 @@ from rattail.util import simple_error
import flet as ft import flet as ft
import wuttapos import wuttapos
from wuttapos.util import get_pos_batch_handler, make_button from wuttapos.util import get_pos_batch_handler
from wuttapos.controls.buttons import make_button
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - buttons
"""
from .base import make_button, WuttaButton, WuttaButtonRow

View file

@ -0,0 +1,82 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - buttons
"""
import flet as ft
def make_button(text, font_size=24, font_bold=True, font_weight=None, **kwargs):
"""
Generic function for making a button.
"""
if 'content' not in kwargs:
if not font_weight and font_bold:
font_weight = ft.FontWeight.BOLD
text = ft.Text(text, size=font_size, weight=font_weight,
text_align=ft.TextAlign.CENTER)
kwargs['content'] = text
return WuttaButton(**kwargs)
class WuttaButton(ft.Container):
"""
Base class for buttons to be shown in the POS menu etc.
"""
def __init__(
self,
pos=None,
pos_cmd=None,
pos_cmd_entry=None,
pos_cmd_kwargs={},
*args, **kwargs
):
kwargs.setdefault('alignment', ft.alignment.center)
kwargs.setdefault('border', ft.border.all(1, 'black'))
kwargs.setdefault('border_radius', ft.border_radius.all(5))
super().__init__(*args, **kwargs)
self.pos = pos
self.pos_cmd = pos_cmd
self.pos_cmd_entry = pos_cmd_entry
self.pos_cmd_kwargs = pos_cmd_kwargs
if not kwargs.get('on_click') and self.pos and self.pos_cmd:
self.on_click = self.handle_click
def handle_click(self, e):
self.pos.cmd(self.pos_cmd, entry=self.pos_cmd_entry,
**self.pos_cmd_kwargs)
class WuttaButtonRow(ft.Row):
"""
Base class for a row of buttons
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('spacing', 0)
super().__init__(*args, **kwargs)

View file

@ -0,0 +1,123 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - buttons
"""
import flet as ft
from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
from wuttapos.controls.menus.meta import WuttaMetaMenu
from wuttapos.controls.menus.context import WuttaContextMenu
from wuttapos.controls.menus.suspend import WuttaSuspendMenu
class WuttaButtonsMaster(ft.Column):
"""
Base class and default implementation for "buttons master"
control. This represents the overall button area in POS view.
"""
def __init__(self, config, pos=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.config = config
self.app = self.config.get_app()
self.pos = pos
self.controls = self.build_controls()
def build_controls(self):
self.tenkey_menu = self.build_tenkey_menu()
self.meta_menu = self.build_meta_menu()
self.context_menu = self.build_context_menu()
self.suspend_menu = self.build_suspend_menu()
return [
ft.Row(
[
self.tenkey_menu,
self.meta_menu,
],
),
ft.Row(
[
self.context_menu,
self.suspend_menu,
],
vertical_alignment=ft.CrossAxisAlignment.START,
),
]
##############################
# tenkey
##############################
def build_tenkey_menu(self):
return WuttaTenkeyMenu(
self.config,
pos=self.pos,
on_char=self.tenkey_char,
on_enter=self.tenkey_enter,
on_up_click=self.tenkey_up_click,
on_up_longpress=self.tenkey_up_longpress,
on_down_click=self.tenkey_down_click,
on_down_longpress=self.tenkey_down_longpress,
)
def tenkey_char(self, key):
self.pos.cmd('entry_append', key)
def tenkey_enter(self, e):
self.pos.cmd('entry_submit')
def tenkey_up_click(self, e):
self.pos.cmd('scroll_up')
def tenkey_up_longpress(self, e):
self.pos.cmd('scroll_up_page')
def tenkey_down_click(self, e):
self.pos.cmd('scroll_down')
def tenkey_down_longpress(self, e):
self.pos.cmd('scroll_down_page')
##############################
# meta
##############################
def build_meta_menu(self):
return WuttaMetaMenu(self.config, pos=self.pos)
##############################
# context
##############################
def build_context_menu(self):
return WuttaContextMenu(self.config, pos=self.pos)
##############################
# suspend
##############################
def build_suspend_menu(self):
return WuttaSuspendMenu(self.config, pos=self.pos)

View file

@ -31,7 +31,7 @@ import flet as ft
import wuttapos import wuttapos
from .timestamp import WuttaTimestamp from .timestamp import WuttaTimestamp
from .feedback import WuttaFeedback from .feedback import WuttaFeedback
from wuttapos.util import make_button from wuttapos.controls.buttons import make_button
class WuttaHeader(ft.Stack): class WuttaHeader(ft.Stack):

View file

@ -26,14 +26,14 @@ WuttaPOS - login form control
import flet as ft import flet as ft
from .keyboard import WuttaKeyboard from wuttapos.controls.buttons import make_button
from .tenkey import WuttaTenkeyMenu from wuttapos.controls.keyboard import WuttaKeyboard
from wuttapos.util import make_button from wuttapos.controls.menus.tenkey import WuttaTenkeyMenu
class WuttaLoginForm(ft.Column): class WuttaLoginForm(ft.Column):
def __init__(self, config, page=None, *args, **kwargs): def __init__(self, config, page=None, pos=None, *args, **kwargs):
self.on_reset = kwargs.pop('on_reset', None) self.on_reset = kwargs.pop('on_reset', None)
# permission to be checked for login to succeed # permission to be checked for login to succeed
@ -63,6 +63,7 @@ class WuttaLoginForm(ft.Column):
self.config = config self.config = config
self.app = config.get_app() self.app = config.get_app()
self.enum = self.app.enum self.enum = self.app.enum
self.pos = pos
# track which login input has focus # track which login input has focus
self.focused = None self.focused = None
@ -77,7 +78,8 @@ class WuttaLoginForm(ft.Column):
[ [
login_form, login_form,
ft.VerticalDivider(), ft.VerticalDivider(),
WuttaTenkeyMenu(self.config, simple=True, WuttaTenkeyMenu(self.config, pos=self.pos,
simple=True,
on_char=self.tenkey_char, on_char=self.tenkey_char,
on_enter=self.tenkey_enter), on_enter=self.tenkey_enter),
], ],

View file

@ -27,7 +27,7 @@ WuttaPOS - base lookup control
import flet as ft import flet as ft
from .keyboard import WuttaKeyboard from .keyboard import WuttaKeyboard
from wuttapos.util import make_button from wuttapos.controls.buttons import make_button
class WuttaLookup(ft.Container): class WuttaLookup(ft.Container):
@ -53,6 +53,9 @@ class WuttaLookup(ft.Container):
self.app = config.get_app() self.app = config.get_app()
self.enum = self.app.enum self.enum = self.app.enum
# TODO: this feels hacky
self.mypage = page
# track current selection # track current selection
self.selected_uuid = None self.selected_uuid = None
self.selected_datarow = None self.selected_datarow = None

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - button menus
"""
from .base import WuttaMenu

View file

@ -0,0 +1,60 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - button menus
"""
import flet as ft
from wuttapos.controls.buttons import make_button
class WuttaMenu(ft.Container):
"""
Base class for button menu controls.
"""
# TODO: should be configurable somehow
default_button_size = 100
default_font_size = 40
def __init__(self, config,
pos=None,
*args, **kwargs):
super().__init__(*args, **kwargs)
self.config = config
self.app = self.config.get_app()
self.pos = pos
self.content = ft.Column(
controls=self.build_controls(),
spacing=0)
def make_button(self, *args, **kwargs):
kwargs.setdefault('font_size', self.default_font_size)
kwargs.setdefault('height', self.default_button_size)
kwargs.setdefault('width', self.default_button_size * 2)
kwargs.setdefault('pos', self.pos)
return make_button(*args, **kwargs)

View file

@ -0,0 +1,57 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - "context" menu
"""
from .base import WuttaMenu
from wuttapos.controls.buttons import WuttaButtonRow
class WuttaContextMenu(WuttaMenu):
def build_controls(self):
return [
WuttaButtonRow([
self.make_button("Cash",
bgcolor='orange',
pos_cmd='tender',
pos_cmd_kwargs={'tender': {'code': 'CA'}}),
self.make_button("Check",
bgcolor='orange',
pos_cmd='tender',
pos_cmd_kwargs={'tender': {'code': 'CK'}}),
]),
WuttaButtonRow([
self.make_button("Food Stamps",
bgcolor='orange',
font_size=34,
pos_cmd='tender',
pos_cmd_kwargs={'tender': {'code': 'FS'}}),
]),
]

View file

@ -0,0 +1,81 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - "meta" menu
"""
from .base import WuttaMenu
from wuttapos.controls.buttons import WuttaButtonRow
class WuttaMetaMenu(WuttaMenu):
def build_controls(self):
return [
WuttaButtonRow([
self.make_button("CUST",
bgcolor='blue',
pos_cmd='customer_dwim'),
self.make_button("VOID",
bgcolor='red',
pos_cmd='void_dwim'),
]),
WuttaButtonRow([
self.make_button("ITEM",
bgcolor='blue',
pos_cmd='item_dwim'),
self.make_button("MGR",
bgcolor='yellow',
pos_cmd='manager_dwim'),
]),
WuttaButtonRow([
self.make_button("OPEN RING",
font_size=32,
bgcolor='blue',
pos_cmd='open_ring_dwim'),
self.make_button("NO SALE",
bgcolor='yellow',
pos_cmd='no_sale_dwim'),
]),
WuttaButtonRow([
self.make_button("Adjust\nPrice",
font_size=30,
bgcolor='yellow',
pos_cmd='adjust_price_dwim'),
self.make_button("REFUND",
bgcolor='red',
pos_cmd='refund_dwim'),
]),
]

View file

@ -0,0 +1,46 @@
# -*- coding: utf-8; -*-
################################################################################
#
# WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023-2024 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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
WuttaPOS - "suspend" menu
"""
from .base import WuttaMenu
from wuttapos.controls.buttons import WuttaButtonRow
class WuttaSuspendMenu(WuttaMenu):
def build_controls(self):
return [
WuttaButtonRow([
self.make_button("SUSPEND",
bgcolor='purple',
pos_cmd='suspend_txn'),
self.make_button("RESUME",
bgcolor='purple',
pos_cmd='resume_txn'),
]),
]

View file

@ -21,21 +21,17 @@
# #
################################################################################ ################################################################################
""" """
WuttaPOS - ten-key control WuttaPOS - ten-key menu
""" """
import flet as ft import flet as ft
from wuttapos.util import make_button from .base import WuttaMenu
class WuttaTenkeyMenu(ft.Container): class WuttaTenkeyMenu(WuttaMenu):
default_font_size = 40 def __init__(self, *args, **kwargs):
default_button_size = 100
def __init__(self, config, page=None, *args, **kwargs):
self.on_reset = kwargs.pop('on_reset', None)
self.simple = kwargs.pop('simple', False) self.simple = kwargs.pop('simple', False)
self.on_char = kwargs.pop('on_char', None) self.on_char = kwargs.pop('on_char', None)
self.on_enter = kwargs.pop('on_enter', None) self.on_enter = kwargs.pop('on_enter', None)
@ -46,9 +42,7 @@ class WuttaTenkeyMenu(ft.Container):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.config = config def build_controls(self):
self.app = config.get_app()
self.enum = self.app.enum
row1 = [ row1 = [
self.make_tenkey_button("1"), self.make_tenkey_button("1"),
@ -94,8 +88,7 @@ class WuttaTenkeyMenu(ft.Container):
self.make_tenkey_button("ENTER", width=self.default_button_size * 2), self.make_tenkey_button("ENTER", width=self.default_button_size * 2),
]) ])
self.content = ft.Column( return [
[
ft.Row( ft.Row(
controls=row1, controls=row1,
spacing=0, spacing=0,
@ -112,38 +105,21 @@ class WuttaTenkeyMenu(ft.Container):
controls=row4, controls=row4,
spacing=0, spacing=0,
), ),
], ]
spacing=0,
)
def informed_refresh(self, **kwargs):
pass
def reset(self, e=None):
if self.on_reset:
self.on_reset(e=e)
def make_tenkey_button( def make_tenkey_button(
self, self,
text, text,
font_size=None,
height=None,
width=None, width=None,
on_click=None,
on_long_press=None, on_long_press=None,
): ):
if not font_size:
font_size = self.default_font_size
if not height:
height = self.default_button_size
if not width: if not width:
width = self.default_button_size width = self.default_button_size
if not on_click:
on_click = self.tenkey_click
return make_button(text, font_size=font_size, bgcolor='green', return self.make_button(text,
height=height, width=width, bgcolor='green',
on_click=on_click, width=width,
on_click=self.tenkey_click,
on_long_press=on_long_press) on_long_press=on_long_press)
def tenkey_click(self, e): def tenkey_click(self, e):

View file

@ -49,7 +49,7 @@ class WuttaTimestamp(ft.Text):
self.running = False self.running = False
def render_time(self, value): def render_time(self, value):
return value.strftime('%a %d %b %Y %I:%M:%S %p') return value.strftime('%a %d %b %Y - %I:%M:%S %p')
async def update_display(self): async def update_display(self):
while self.running: while self.running:

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# WuttaPOS -- Pythonic Point of Sale System # WuttaPOS -- Pythonic Point of Sale System
# Copyright © 2023 Lance Edgar # Copyright © 2023-2024 Lance Edgar
# #
# This file is part of WuttaPOS. # This file is part of WuttaPOS.
# #
@ -38,21 +38,6 @@ def get_pos_batch_handler(config):
return app.get_batch_handler('pos', default='rattail.batch.pos:POSBatchHandler') return app.get_batch_handler('pos', default='rattail.batch.pos:POSBatchHandler')
def make_button(text, font_size=24, font_bold=True, font_weight=None, **kwargs):
"""
Generic function for making a Container button.
"""
if not font_weight and font_bold:
font_weight = ft.FontWeight.BOLD
text = ft.Text(text, size=font_size, weight=font_weight,
text_align=ft.TextAlign.CENTER)
kwargs.setdefault('alignment', ft.alignment.center)
kwargs.setdefault('border', ft.border.all(1, 'black'))
kwargs.setdefault('border_radius', ft.border_radius.all(5))
return ft.Container(content=text, **kwargs)
def show_snackbar(page, text, bgcolor='yellow'): def show_snackbar(page, text, bgcolor='yellow'):
page.snack_bar = ft.SnackBar(ft.Text(text, color='black', page.snack_bar = ft.SnackBar(ft.Text(text, color='black',
size=40, size=40,

View file

@ -31,7 +31,8 @@ from rattail.files import resource_path
import flet as ft import flet as ft
from wuttapos.controls.header import WuttaHeader from wuttapos.controls.header import WuttaHeader
from wuttapos.util import get_pos_batch_handler, make_button, show_snackbar from wuttapos.controls.buttons import make_button
from wuttapos.util import get_pos_batch_handler, show_snackbar
class WuttaView(ft.View): class WuttaView(ft.View):

File diff suppressed because it is too large Load diff