mirror of
https://github.com/rr-/pq-cli.git
synced 2026-09-28 22:33:59 -06:00
ui/curses: implement game view widget focusing
This commit is contained in:
@@ -6,6 +6,7 @@ import typing as T
|
||||
from pqcli.mechanic import Player, Simulation
|
||||
from pqcli.roster import Roster
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
from pqcli.ui.curses.widgets import Widget
|
||||
|
||||
from ..base_view import BaseView
|
||||
from .character_sheet_window import CharacterSheetWindow
|
||||
@@ -36,8 +37,47 @@ class GameView(BaseView):
|
||||
self._last_tick = datetime.datetime.now()
|
||||
|
||||
def keypress(self, key: int) -> None:
|
||||
focused = self.focused
|
||||
|
||||
if key in map(ord, "qQ"):
|
||||
self.on_exit()
|
||||
|
||||
elif key in map(ord, "jJ"):
|
||||
if focused == self._char_sheet_win:
|
||||
self.focus(self._spell_book_win)
|
||||
elif focused == self._equipment_win:
|
||||
self.focus(self._inventory_win)
|
||||
elif focused == self._plot_win:
|
||||
self.focus(self._quest_book_win)
|
||||
|
||||
elif key in map(ord, "kK"):
|
||||
if focused == self._spell_book_win:
|
||||
self.focus(self._char_sheet_win)
|
||||
elif focused == self._inventory_win:
|
||||
self.focus(self._equipment_win)
|
||||
elif focused == self._quest_book_win:
|
||||
self.focus(self._plot_win)
|
||||
|
||||
elif key in map(ord, "hH"):
|
||||
if focused == self._equipment_win:
|
||||
self.focus(self._char_sheet_win)
|
||||
elif focused == self._inventory_win:
|
||||
self.focus(self._spell_book_win)
|
||||
elif focused == self._plot_win:
|
||||
self.focus(self._equipment_win)
|
||||
elif focused == self._quest_book_win:
|
||||
self.focus(self._inventory_win)
|
||||
|
||||
elif key in map(ord, "lL"):
|
||||
if focused == self._char_sheet_win:
|
||||
self.focus(self._equipment_win)
|
||||
elif focused == self._spell_book_win:
|
||||
self.focus(self._inventory_win)
|
||||
elif focused == self._equipment_win:
|
||||
self.focus(self._plot_win)
|
||||
elif focused == self._inventory_win:
|
||||
self.focus(self._quest_book_win)
|
||||
|
||||
else:
|
||||
super().keypress(key)
|
||||
|
||||
@@ -135,14 +175,19 @@ class GameView(BaseView):
|
||||
x=0,
|
||||
)
|
||||
|
||||
self._children: T.List[Widget] = [
|
||||
self._char_sheet_win,
|
||||
self._spell_book_win,
|
||||
self._equipment_win,
|
||||
self._inventory_win,
|
||||
self._plot_win,
|
||||
self._quest_book_win,
|
||||
self._task_win,
|
||||
]
|
||||
|
||||
def stop(self) -> None:
|
||||
self._char_sheet_win.stop()
|
||||
self._spell_book_win.stop()
|
||||
self._equipment_win.stop()
|
||||
self._inventory_win.stop()
|
||||
self._plot_win.stop()
|
||||
self._quest_book_win.stop()
|
||||
self._task_win.stop()
|
||||
for child in self._children:
|
||||
child.stop()
|
||||
del self._col1_win
|
||||
del self._col2_win
|
||||
del self._col3_win
|
||||
@@ -154,3 +199,14 @@ class GameView(BaseView):
|
||||
self._last_tick = datetime.datetime.now()
|
||||
if self._args.use_saves:
|
||||
self._roster.save_periodically()
|
||||
|
||||
@property
|
||||
def focused(self) -> Widget:
|
||||
for widget in self._children:
|
||||
if widget.focused:
|
||||
return widget
|
||||
return None
|
||||
|
||||
def focus(self, widget: Widget):
|
||||
self.focused.focused = False
|
||||
widget.focused = True
|
||||
|
||||
@@ -2,16 +2,20 @@ import typing as T
|
||||
|
||||
from pqcli.mechanic import Player, StatType
|
||||
|
||||
from .focusable import Focusable
|
||||
from .progress_bar_window import DataTableProgressBarWindow
|
||||
|
||||
|
||||
class CharacterSheetWindow(DataTableProgressBarWindow):
|
||||
class CharacterSheetWindow(Focusable, DataTableProgressBarWindow):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(
|
||||
parent, h, w, y, x, " Character Sheet ", align_right=False
|
||||
)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._focused = True
|
||||
|
||||
self._player = player
|
||||
self._player.connect("level_up", self._sync_traits)
|
||||
@@ -47,7 +51,9 @@ class CharacterSheetWindow(DataTableProgressBarWindow):
|
||||
self._render_data_table()
|
||||
|
||||
def _sync_exp(self) -> None:
|
||||
cur = self._player.exp_bar.position
|
||||
max_ = self._player.exp_bar.max_
|
||||
text = f"Experience ({max_-cur:.0f} XP to go)"
|
||||
self._render_progress_bar(cur, max_, text)
|
||||
self._cur_pos = self._player.exp_bar.position
|
||||
self._max_pos = self._player.exp_bar.max_
|
||||
self._progress_title = (
|
||||
f"Experience ({self._max_pos-self._cur_pos:.0f} XP to go)"
|
||||
)
|
||||
self._render_progress_bar()
|
||||
|
||||
@@ -3,12 +3,15 @@ import typing as T
|
||||
from pqcli.mechanic import EquipmentType, Player
|
||||
from pqcli.ui.curses.widgets import DataTable, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
|
||||
class EquipmentWindow(WindowWrapper):
|
||||
|
||||
class EquipmentWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._data_table = DataTable(
|
||||
self._win, h - 2, w - 2, 1, 1, align_right=False
|
||||
@@ -44,9 +47,11 @@ class EquipmentWindow(WindowWrapper):
|
||||
if not self._win:
|
||||
return
|
||||
|
||||
self._win.box()
|
||||
text = " Equipment "
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1]))
|
||||
with self._focus_standout(self._win):
|
||||
self._win.box()
|
||||
text = " Equipment "
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1]))
|
||||
|
||||
self._win.refresh()
|
||||
self._data_table.render()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import contextlib
|
||||
import typing as T
|
||||
|
||||
from pqcli.ui.curses.event_handler import EventHandler
|
||||
|
||||
|
||||
class Focusable:
|
||||
def __init__(self, *args: T.Any, **kwargs: T.Any) -> None:
|
||||
self._focused = False
|
||||
self._on_focus_change = EventHandler()
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def focused(self) -> bool:
|
||||
return self._focused
|
||||
|
||||
@focused.setter
|
||||
def focused(self, focused: bool) -> None:
|
||||
self._focused = focused
|
||||
self._on_focus_change()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _focus_standout(self, win: T.Any) -> T.Generator:
|
||||
if self._focused:
|
||||
self._win.standout()
|
||||
yield
|
||||
if self._focused:
|
||||
self._win.standend()
|
||||
@@ -5,14 +5,16 @@ from pqcli.lingo import to_roman
|
||||
from pqcli.mechanic import InventoryItem, Player
|
||||
from pqcli.ui.curses.widgets import DataTable, ProgressBar, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
from .progress_bar_window import DataTableProgressBarWindow
|
||||
|
||||
|
||||
class InventoryWindow(DataTableProgressBarWindow):
|
||||
class InventoryWindow(Focusable, DataTableProgressBarWindow):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x, " Inventory ", align_right=True)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._player = player
|
||||
self._player.connect("new_task", self._on_new_task)
|
||||
@@ -46,10 +48,12 @@ class InventoryWindow(DataTableProgressBarWindow):
|
||||
self._sync_encumbrance()
|
||||
|
||||
def _sync_encumbrance(self) -> None:
|
||||
cur = self._player.inventory.encum_bar.position
|
||||
max_ = self._player.inventory.encum_bar.max_
|
||||
text = f"Encumbrance ({cur:.0f}/{max_} cubits)"
|
||||
self._render_progress_bar(cur, max_, text)
|
||||
self._cur_pos = self._player.inventory.encum_bar.position
|
||||
self._max_pos = self._player.inventory.encum_bar.max_
|
||||
self._progress_title = (
|
||||
f"Encumbrance ({self._cur_pos:.0f}/{self._max_pos} cubits)"
|
||||
)
|
||||
self._render_progress_bar()
|
||||
|
||||
def _sync_gold(self) -> None:
|
||||
self._data_table.set("Gold", str(self._player.inventory.gold))
|
||||
|
||||
@@ -4,16 +4,18 @@ from pqcli.lingo import act_name
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import ListBox, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
from .progress_bar_window import ListBoxProgressBarWindow
|
||||
|
||||
|
||||
class PlotWindow(ListBoxProgressBarWindow):
|
||||
class PlotWindow(Focusable, ListBoxProgressBarWindow):
|
||||
cutoff = 100
|
||||
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x, " Plot Development ")
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._player = player
|
||||
self._player.quest_book.connect("start_act", self._sync_act_add)
|
||||
@@ -51,8 +53,6 @@ class PlotWindow(ListBoxProgressBarWindow):
|
||||
self._list_box.add("[ ] " + act_name(act_number))
|
||||
|
||||
def _sync_position(self) -> None:
|
||||
self._render_progress_bar(
|
||||
self._player.quest_book.plot_bar.position,
|
||||
self._player.quest_book.plot_bar.max_,
|
||||
"",
|
||||
)
|
||||
self._cur_pos = self._player.quest_book.plot_bar.position
|
||||
self._max_pos = self._player.quest_book.plot_bar.max_
|
||||
self._render_progress_bar()
|
||||
|
||||
@@ -8,6 +8,8 @@ from pqcli.ui.curses.widgets import (
|
||||
WindowWrapper,
|
||||
)
|
||||
|
||||
from .focusable import Focusable
|
||||
|
||||
|
||||
class ProgressBarWindow(WindowWrapper):
|
||||
def __init__(
|
||||
@@ -16,6 +18,9 @@ class ProgressBarWindow(WindowWrapper):
|
||||
super().__init__(parent, h, w, y, x)
|
||||
|
||||
self._title = title
|
||||
self._cur_pos = 0
|
||||
self._max_pos = 1
|
||||
self._progress_title = ""
|
||||
|
||||
try:
|
||||
self._progress_bar_win = (
|
||||
@@ -34,27 +39,41 @@ class ProgressBarWindow(WindowWrapper):
|
||||
del self._progress_bar_win
|
||||
self._progress_bar_win = None
|
||||
|
||||
def _render_progress_bar(self, cur: float, max_: float, text: str) -> None:
|
||||
def _render_progress_bar(self) -> None:
|
||||
if not self._progress_bar_win:
|
||||
return
|
||||
|
||||
self._progress_bar_win.erase()
|
||||
self._progress_bar_win.border(
|
||||
curses.ACS_VLINE,
|
||||
curses.ACS_VLINE,
|
||||
curses.ACS_HLINE,
|
||||
curses.ACS_HLINE,
|
||||
curses.ACS_LTEE,
|
||||
curses.ACS_RTEE,
|
||||
curses.ACS_LLCORNER,
|
||||
curses.ACS_LRCORNER,
|
||||
)
|
||||
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._progress_bar_win.addnstr(
|
||||
0, x, text, min(len(text), self._progress_bar_win.getmaxyx()[1])
|
||||
)
|
||||
self._progress_bar.set_position(cur, max_)
|
||||
if self._focused:
|
||||
# with self._focus_standout(self._progress_bar_win):
|
||||
self._progress_bar_win.standout()
|
||||
|
||||
if True:
|
||||
self._progress_bar_win.border(
|
||||
curses.ACS_VLINE,
|
||||
curses.ACS_VLINE,
|
||||
curses.ACS_HLINE,
|
||||
curses.ACS_HLINE,
|
||||
curses.ACS_LTEE,
|
||||
curses.ACS_RTEE,
|
||||
curses.ACS_LLCORNER,
|
||||
curses.ACS_LRCORNER,
|
||||
)
|
||||
|
||||
text = self._progress_title
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._progress_bar_win.addnstr(
|
||||
0,
|
||||
x,
|
||||
text,
|
||||
min(len(text), self._progress_bar_win.getmaxyx()[1]),
|
||||
)
|
||||
|
||||
if self._focused:
|
||||
self._progress_bar_win.standend()
|
||||
|
||||
self._progress_bar.set_position(self._cur_pos, self._max_pos)
|
||||
self._progress_bar_win.refresh()
|
||||
|
||||
|
||||
@@ -83,14 +102,20 @@ class DataTableProgressBarWindow(ProgressBarWindow):
|
||||
if not self._win:
|
||||
return
|
||||
|
||||
self._win.box()
|
||||
x = max(0, (self.getmaxyx()[1] - len(self._title)) // 2)
|
||||
self._win.addnstr(
|
||||
0, x, self._title, min(len(self._title), self.getmaxyx()[1])
|
||||
)
|
||||
with self._focus_standout(self._win):
|
||||
self._win.box()
|
||||
x = max(0, (self.getmaxyx()[1] - len(self._title)) // 2)
|
||||
self._win.addnstr(
|
||||
0, x, self._title, min(len(self._title), self.getmaxyx()[1])
|
||||
)
|
||||
|
||||
self._win.refresh()
|
||||
self._data_table.render()
|
||||
|
||||
def _render(self) -> None:
|
||||
self._render_data_table()
|
||||
self._render_progress_bar()
|
||||
|
||||
|
||||
class ListBoxProgressBarWindow(ProgressBarWindow):
|
||||
def __init__(
|
||||
@@ -108,10 +133,16 @@ class ListBoxProgressBarWindow(ProgressBarWindow):
|
||||
if not self._win:
|
||||
return
|
||||
|
||||
self._win.box()
|
||||
x = max(0, (self.getmaxyx()[1] - len(self._title)) // 2)
|
||||
self._win.addnstr(
|
||||
0, x, self._title, min(len(self._title), self.getmaxyx()[1])
|
||||
)
|
||||
with self._focus_standout(self._win):
|
||||
self._win.box()
|
||||
x = max(0, (self.getmaxyx()[1] - len(self._title)) // 2)
|
||||
self._win.addnstr(
|
||||
0, x, self._title, min(len(self._title), self.getmaxyx()[1])
|
||||
)
|
||||
|
||||
self._win.refresh()
|
||||
self._list_box.render()
|
||||
|
||||
def _render(self) -> None:
|
||||
self._render_list_box()
|
||||
self._render_progress_bar()
|
||||
|
||||
@@ -3,16 +3,18 @@ import typing as T
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import ListBox, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
from .progress_bar_window import ListBoxProgressBarWindow
|
||||
|
||||
|
||||
class QuestBookWindow(ListBoxProgressBarWindow):
|
||||
class QuestBookWindow(Focusable, ListBoxProgressBarWindow):
|
||||
cutoff = 100
|
||||
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x, " Quests ")
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._player = player
|
||||
self._player.quest_book.connect("start_quest", self._sync_quest_add)
|
||||
@@ -49,8 +51,6 @@ class QuestBookWindow(ListBoxProgressBarWindow):
|
||||
self._list_box.add("[ ] " + quest_name)
|
||||
|
||||
def _sync_position(self) -> None:
|
||||
self._render_progress_bar(
|
||||
self._player.quest_book.quest_bar.position,
|
||||
self._player.quest_book.quest_bar.max_,
|
||||
"",
|
||||
)
|
||||
self._cur_pos = self._player.quest_book.quest_bar.position
|
||||
self._max_pos = self._player.quest_book.quest_bar.max_
|
||||
self._render_progress_bar()
|
||||
|
||||
@@ -4,12 +4,15 @@ from pqcli.lingo import to_roman
|
||||
from pqcli.mechanic import Player, Spell
|
||||
from pqcli.ui.curses.widgets import DataTable, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
|
||||
class SpellBookWindow(WindowWrapper):
|
||||
|
||||
class SpellBookWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
super().__init__(parent, h, w, y, x)
|
||||
self._on_focus_change += self._render
|
||||
|
||||
self._data_table = DataTable(
|
||||
self._win, h - 2, w - 2, 1, 1, align_right=True
|
||||
@@ -46,9 +49,11 @@ class SpellBookWindow(WindowWrapper):
|
||||
if not self._win:
|
||||
return
|
||||
|
||||
self._win.box()
|
||||
text = " Spell Book "
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1]))
|
||||
with self._focus_standout(self._win):
|
||||
self._win.box()
|
||||
text = " Spell Book "
|
||||
x = max(0, (self.getmaxyx()[1] - len(text)) // 2)
|
||||
self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1]))
|
||||
|
||||
self._win.refresh()
|
||||
self._data_table.render()
|
||||
|
||||
@@ -3,8 +3,10 @@ import typing as T
|
||||
from pqcli.mechanic import Player
|
||||
from pqcli.ui.curses.widgets import Label, ProgressBar, WindowWrapper
|
||||
|
||||
from .focusable import Focusable
|
||||
|
||||
class TaskProgressWindow(WindowWrapper):
|
||||
|
||||
class TaskProgressWindow(Focusable, WindowWrapper):
|
||||
def __init__(
|
||||
self, player: Player, parent: T.Any, h: int, w: int, y: int, x: int
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user