diff --git a/src/mind_reader/ui/views/session_view.py b/src/mind_reader/ui/views/session_view.py new file mode 100644 index 0000000..c858cc9 --- /dev/null +++ b/src/mind_reader/ui/views/session_view.py @@ -0,0 +1,220 @@ +import sys +from dataclasses import dataclass +from enum import Enum, auto +from typing import Optional + +import wx + +from mind_reader.core import SessionState + + +def enable_high_dpi_support(): + if sys.platform == "win32": + try: + import ctypes + + ctypes.windll.shcore.SetProcessDpiAwareness(2) + except Exception: + try: + import ctypes + + ctypes.windll.user32.SetProcessDPIAware() + except Exception: + pass + + +class CardState(Enum): + SETTER_NOT_SET = auto() + SETTER_HIDDEN = auto() + SETTER_REVEALED = auto() + + GUESSER_DISABLED = auto() + GUESSER_WAITING = auto() + GUESSER_CORRECT = auto() + GUESSER_WRONG = auto() + + +class ArrowDirection(Enum): + LEFT = auto() + RIGHT = auto() + + +@dataclass(frozen=True) +class SetterCardColour: + NOT_SET = wx.Colour(82, 97, 107) + HIDDEN = wx.Colour(201, 214, 223) + REVEALED = wx.Colour(240, 245, 249) + + +@dataclass(frozen=True) +class GuesserCardColour: + DISABLED = wx.Colour(82, 97, 107) + WAITING = wx.Colour(201, 214, 223) + CORRECT = wx.Colour(76, 175, 80) + WRONG = wx.Colour(229, 115, 115) + + +@dataclass(frozen=True) +class ArrowSymbol: + LEFT: str = "◀" + RIGHT: str = "▶" + + +class Card(wx.Panel): + def __init__(self, parent, state: CardState): + super().__init__(parent, style=wx.BORDER_SIMPLE) + self._direction: Optional[ArrowDirection] = None + self._state = state + self._setup() + self._update() + + def _setup(self): + self.SetMinSize(wx.Size(35, 35)) + + self.label = wx.StaticText(self, style=wx.ALIGN_CENTER) + + # font = self.label.GetFont() + # font.SetPointSize(6) + # self.label.SetFont(font) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.AddStretchSpacer() + sizer.Add(self.label, 0, wx.ALIGN_CENTER) + sizer.AddStretchSpacer() + + self.SetSizer(sizer) + + def _update(self): + match self._state: + case CardState.SETTER_NOT_SET: + colour = SetterCardColour.NOT_SET + show_arrow = False + + case CardState.SETTER_HIDDEN: + colour = SetterCardColour.HIDDEN + show_arrow = False + + case CardState.SETTER_REVEALED: + colour = SetterCardColour.REVEALED + show_arrow = True + + case CardState.GUESSER_DISABLED: + colour = GuesserCardColour.DISABLED + show_arrow = False + + case CardState.GUESSER_WAITING: + colour = GuesserCardColour.WAITING + show_arrow = False + + case CardState.GUESSER_CORRECT: + colour = GuesserCardColour.CORRECT + show_arrow = True + + case CardState.GUESSER_WRONG: + colour = GuesserCardColour.WRONG + show_arrow = True + + match self._direction: + case ArrowDirection.LEFT: + symbol = ArrowSymbol.LEFT + + case ArrowDirection.RIGHT: + symbol = ArrowSymbol.RIGHT + + if show_arrow and self._direction: + match self._direction: + case ArrowDirection.LEFT: + symbol = ArrowSymbol.LEFT + + case ArrowDirection.RIGHT: + symbol = ArrowSymbol.RIGHT + + self.label.SetLabel(symbol) + + else: + self.label.SetLabel("") + + self.SetBackgroundColour(colour) + + self.Layout() + self.Refresh() + + def set( + self, + state: CardState, + direction: Optional[ArrowDirection] = None, + ) -> None: + self._state = state + self._direction = direction + self._update() + + +class SessionPanel(wx.Panel): + def __init__(self, parent, sequence_length: int = 20): + super().__init__(parent) + self._sequence_length = sequence_length + self._setter_sequence: list[Card] = [] + self._guesser_sequence: list[Card] = [] + + self._setup() + + def _setup(self) -> None: + main_sizer = wx.BoxSizer(wx.VERTICAL) + + setter_sizer = wx.BoxSizer(wx.HORIZONTAL) + for _ in range(self._sequence_length): + card = Card(self, CardState.SETTER_NOT_SET) + setter_sizer.Add(card, 0, wx.ALL, 3) + self._setter_sequence.append(card) + + guesser_sizer = wx.BoxSizer(wx.HORIZONTAL) + for _ in range(self._sequence_length): + card = Card(self, CardState.GUESSER_DISABLED) + guesser_sizer.Add(card, 0, wx.ALL, 3) + self._guesser_sequence.append(card) + + main_sizer.Add(setter_sizer, 0, wx.ALL, 5) + main_sizer.Add(guesser_sizer, 0, wx.ALL, 5) + self.SetSizer(main_sizer) + + def update_setter_card( + self, + index: int, + state: CardState, + direction: Optional[ArrowDirection] = None, + ) -> None: + self._setter_sequence[index].set(state, direction) + + def update_guesser_card( + self, + index: int, + state: CardState, + direction: Optional[ArrowDirection] = None, + ) -> None: + self._guesser_sequence[index].set(state, direction) + + +class TestFrame(wx.Frame): + def __init__(self): + super().__init__(None, title="TestFrame", size=wx.Size(900, 500)) + main_panel = SessionPanel(self) + main_sizer = wx.BoxSizer(wx.VERTICAL) + main_sizer.Add(main_panel, 1, wx.EXPAND) + self.SetSizer(main_sizer) + self.Layout() + + main_panel.update_setter_card( + 0, CardState.SETTER_REVEALED, ArrowDirection.LEFT + ) + + main_panel.update_guesser_card( + 0, CardState.GUESSER_WRONG, ArrowDirection.RIGHT + ) + + +if __name__ == "__main__": + enable_high_dpi_support() + app = wx.App() + frame = TestFrame() + frame.Show() + app.MainLoop()