Skip to content
Snippets Groups Projects

Refactor Cart into checkerboard table

Merged Mateusz Kudela requested to merge mkudela/issue-173 into develop
Compare and Show latest version
1 file
+ 58
28
Compare changes
  • Side-by-side
  • Inline
@@ -10,6 +10,7 @@ from textual.reactive import reactive
from textual.widgets import Static
from clive.__private.core.formatters.humanize import humanize_operation_details, humanize_operation_name
from clive.__private.logger import logger
from clive.__private.ui.get_css import get_relative_css_path
from clive.__private.ui.shared.base_screen import BaseScreen
from clive.__private.ui.transaction_summary import TransactionSummaryFromCart
@@ -60,6 +61,12 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
Binding("ctrl+down", "select_next", "Next"),
]
class RemovalCheck(Message):
"""Message for checking if removal is in progress."""
def __init__(self, widget: CartItem) -> None:
self.widget = widget
super().__init__()
class Delete(Message):
def __init__(self, widget: CartItem) -> None:
self.widget = widget
@@ -79,10 +86,13 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
self.focus_button = focus_button
super().__init__()
class StartRemovalProcess(Message):
"""Message to prevent sending duplicated Delete message."""
def __init__(self, operation_idx: int) -> None:
self.__idx = operation_idx
self._idx = operation_idx
assert self.is_valid(self.__idx), "During construction, index has to be valid"
assert self.is_valid(self._idx), "During construction, index has to be valid"
self._index_cell = CliveCheckerBoardTableCell(self.get_operation_index(), classes="index")
super().__init__(
self._index_cell,
@@ -95,12 +105,13 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
classes="actions",
),
)
self._ready_for_deletion = False
def __repr__(self) -> str:
return f"{self.__class__.__name__}(idx={self.__idx})"
return f"{self.__class__.__name__}(idx={self._idx})"
def on_mount(self) -> None:
self.reactive_idx = self.__idx
self.reactive_idx = self._idx
if self.__is_first:
self.unbind("ctrl+up")
elif self.__is_last:
@@ -108,30 +119,30 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
def watch_reactive_idx(self, idx: int) -> None:
assert self.is_valid(idx), "idx is invalid when trying to update."
self.__idx = idx
self._idx = idx
self._index_cell.update_content(self.get_operation_index())
def is_valid(self, idx: int) -> bool:
return idx < self.__operations_count
def get_operation_index(self) -> str:
return f"{self.__idx + 1}." if self.is_valid(self.idx) else "?"
return f"{self._idx + 1}." if self.is_valid(self.idx) else "?"
def get_operation_name(self) -> str:
return humanize_operation_name(self.operation) if self.is_valid(self.idx) else "?"
def action_select_previous(self) -> None:
focused = self.is_any_button_focused()
self.post_message(self.Focus(target_idx=self.__idx - 1, focus_button=focused))
focused = self.get_focused_button_or_none()
self.post_message(self.Focus(target_idx=self._idx - 1, focus_button=focused))
def action_select_next(self) -> None:
focused = self.is_any_button_focused()
self.post_message(self.Focus(target_idx=self.__idx + 1, focus_button=focused))
focused = self.get_focused_button_or_none()
self.post_message(self.Focus(target_idx=self._idx + 1, focus_button=focused))
def get_operation_details(self) -> str:
return humanize_operation_details(self.operation) if self.is_valid(self.idx) else "?"
def is_any_button_focused(self) -> str | None:
def get_focused_button_or_none(self) -> str | None:
buttons = self.query(CliveButton)
for button in buttons:
if button.has_focus:
@@ -140,12 +151,20 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
@property
def idx(self) -> int:
return self.__idx
return self._idx
@property
def operation(self) -> OperationBaseClass:
assert self.is_valid(self.idx), "cannot get operation, position is invalid"
return self.profile.cart[self.__idx]
assert self.is_valid(self._idx), "cannot get operation, position is invalid"
return self.profile.cart[self._idx]
@property
def ready_for_deletion(self) -> bool:
return self._ready_for_deletion
@ready_for_deletion.setter
def ready_for_deletion(self, value: bool) -> None:
self._ready_for_deletion = value
@property
def __operations_count(self) -> int:
@@ -153,32 +172,32 @@ class CartItem(CliveCheckerboardTableRow, CliveWidget, can_focus=True):
@property
def __is_first(self) -> bool:
return self.__idx == 0
return self._idx == 0
@property
def __is_last(self) -> bool:
return self.__idx == self.__operations_count - 1
return self._idx == self.__operations_count - 1
@on(CliveButton.Pressed, "#move-up-button")
def move_up(self) -> None:
self.post_message(self.Move(from_idx=self.__idx, to_idx=self.__idx - 1))
self.post_message(self.Move(from_idx=self._idx, to_idx=self._idx - 1))
@on(CliveButton.Pressed, "#move-down-button")
def move_down(self) -> None:
self.post_message(self.Move(from_idx=self.__idx, to_idx=self.__idx + 1))
self.post_message(self.Move(from_idx=self._idx, to_idx=self._idx + 1))
@on(CliveButton.Pressed, "#delete-button")
def delete(self) -> None:
cart = self.app.query_one(Cart)
if cart.ready_to_delete_item:
cart.toggle_removal_in_progress()
self.post_message(self.RemovalCheck(self))
logger.debug(f"DELETE CLICKED, READY?: {self._ready_for_deletion}")
if self._ready_for_deletion:
self.post_message(self.StartRemovalProcess())
self.post_message(self.Delete(self))
@on(Move)
def move_item(self, event: CartItem.Move) -> None:
if event.to_idx == self.__idx:
self.__idx = event.from_idx
if event.to_idx == self._idx:
self._idx = event.from_idx
self.app.trigger_profile_watchers()
@@ -228,8 +247,12 @@ class Cart(BaseScreen):
with self.__scrollable_part:
yield self._cart_table
def toggle_removal_in_progress(self) -> None:
self._removal_in_progress = not self._removal_in_progress
def _start_removal_process(self) -> None:
self._removal_in_progress = True
def _end_removal_process(self) -> None:
self.app.trigger_profile_watchers()
self._removal_in_progress = False
async def __rebuild_items(self, from_index: int = 0, to_index: int | None = None) -> None:
await self._cart_table.rebuild(starting_from_element=from_index, ending_with_element=to_index)
@@ -260,13 +283,20 @@ class Cart(BaseScreen):
for cell, value in zip(cells, data):
cell.update_content(value) # type: ignore[arg-type]
@on(CartItem.RemovalCheck)
def check_removal(self, event: CartItem.RemovalCheck) -> None:
event.widget.ready_for_deletion = self.ready_to_delete_item
@on(CartItem.StartRemovalProcess)
def start_removal_process(self) -> None:
self._start_removal_process()
@on(CartItem.Delete)
async def remove_item(self, event: CartItem.Delete) -> None:
await self.query(CliveCheckerboardTableRow)[event.widget.reactive_idx].remove()
self.profile.cart.remove(event.widget.operation)
self._handle_remove_event(triggering_widget=event.widget)
self.app.trigger_profile_watchers()
self._removal_in_progress = False
self._end_removal_process()
if len(self.app.world.profile.cart) > 0 and event.widget.reactive_idx == 0:
# disable first ButtomMoveUp if first element was removed
Loading