ui/curses: highlight changed and added items

This commit is contained in:
rr-
2019-01-14 16:10:26 +01:00
parent 5f34a85c80
commit 555f386b24
9 changed files with 50 additions and 3 deletions
+2
View File
@@ -12,6 +12,7 @@ from pqcli.roster import Roster
from pqcli.ui.base import BaseUserInterface from pqcli.ui.base import BaseUserInterface
from pqcli.ui.curses.colors import ( from pqcli.ui.curses.colors import (
COLOR_FOCUSED, COLOR_FOCUSED,
COLOR_HIGHLIGHT,
COLOR_LOGO, COLOR_LOGO,
COLOR_LOGO_ALT, COLOR_LOGO_ALT,
COLOR_PROGRESSBAR, COLOR_PROGRESSBAR,
@@ -57,6 +58,7 @@ class CursesUserInterface(BaseUserInterface):
curses.init_pair(COLOR_SCROLLBAR_THUMB, -1, 7) curses.init_pair(COLOR_SCROLLBAR_THUMB, -1, 7)
curses.init_pair(COLOR_SCROLLBAR_TRACK, -1, 0) curses.init_pair(COLOR_SCROLLBAR_TRACK, -1, 0)
curses.init_pair(COLOR_PROGRESSBAR, 0, 9) curses.init_pair(COLOR_PROGRESSBAR, 0, 9)
curses.init_pair(COLOR_HIGHLIGHT, 3, -1)
def signal_handler(sig: T.Any, frame: T.Any) -> None: def signal_handler(sig: T.Any, frame: T.Any) -> None:
if self.args.use_saves: if self.args.use_saves:
+1
View File
@@ -6,6 +6,7 @@ COLOR_FOCUSED = 3
COLOR_SCROLLBAR_TRACK = 4 COLOR_SCROLLBAR_TRACK = 4
COLOR_SCROLLBAR_THUMB = 5 COLOR_SCROLLBAR_THUMB = 5
COLOR_PROGRESSBAR = 6 COLOR_PROGRESSBAR = 6
COLOR_HIGHLIGHT = 7
def has_colors() -> bool: def has_colors() -> bool:
@@ -16,7 +16,7 @@ class EquipmentWindow(Focusable, WindowWrapper):
) )
self._player = player self._player = player
self._player.equipment.connect("change", self.sync_equipment_change) self._player.equipment.connect("change", self._sync_equipment_change)
self.sync() self.sync()
@@ -24,7 +24,9 @@ class EquipmentWindow(Focusable, WindowWrapper):
super().stop() super().stop()
self._data_table.stop() self._data_table.stop()
self._player.equipment.disconnect("change", self.sync_equipment_change) self._player.equipment.disconnect(
"change", self._sync_equipment_change
)
def sync(self) -> None: def sync(self) -> None:
self._data_table.clear() self._data_table.clear()
@@ -33,12 +35,14 @@ class EquipmentWindow(Focusable, WindowWrapper):
equipment_type.value.ljust(15), equipment_type.value.ljust(15),
self._player.equipment[equipment_type] or "", self._player.equipment[equipment_type] or "",
) )
self._data_table.select(None)
self._render() self._render()
def sync_equipment_change( def _sync_equipment_change(
self, equipment_type: EquipmentType, item_name: T.Optional[str] self, equipment_type: EquipmentType, item_name: T.Optional[str]
) -> None: ) -> None:
self._data_table.set(equipment_type.value, item_name or "") self._data_table.set(equipment_type.value, item_name or "")
self._data_table.select(equipment_type.value)
self._render() self._render()
def _render(self) -> None: def _render(self) -> None:
@@ -78,6 +78,7 @@ class InventoryWindow(Focusable, DataTableProgressBarWindow):
def _sync_gold(self) -> None: def _sync_gold(self) -> None:
self._data_table.set("Gold", str(self._player.inventory.gold)) self._data_table.set("Gold", str(self._player.inventory.gold))
self._data_table.select("Gold")
self._render_data_table() self._render_data_table()
def _sync_items(self) -> None: def _sync_items(self) -> None:
@@ -86,18 +87,22 @@ class InventoryWindow(Focusable, DataTableProgressBarWindow):
for item in self._player.inventory: for item in self._player.inventory:
self._data_table.add(item.name, str(item.quantity)) self._data_table.add(item.name, str(item.quantity))
self._data_table.scroll_to_item(0) self._data_table.scroll_to_item(0)
self._data_table.select(None)
self._render_data_table() self._render_data_table()
def _sync_item_add(self, item: InventoryItem) -> None: def _sync_item_add(self, item: InventoryItem) -> None:
self._data_table.add(item.name, str(item.quantity)) self._data_table.add(item.name, str(item.quantity))
self._data_table.select(item.name)
self._render_data_table() self._render_data_table()
def _sync_item_del(self, item: InventoryItem) -> None: def _sync_item_del(self, item: InventoryItem) -> None:
self._data_table.delete(item.name) self._data_table.delete(item.name)
self._data_table.select(None)
self._render_data_table() self._render_data_table()
def _sync_item_change(self, item: InventoryItem) -> None: def _sync_item_change(self, item: InventoryItem) -> None:
self._data_table.set(item.name, str(item.quantity)) self._data_table.set(item.name, str(item.quantity))
self._data_table.select(item.name)
self._render_data_table() self._render_data_table()
def _on_new_task(self) -> None: def _on_new_task(self) -> None:
@@ -60,6 +60,7 @@ class PlotWindow(Focusable, ListBoxProgressBarWindow):
if prev is not None: if prev is not None:
self._list_box.set(-1, "[X] " + prev[4:]) self._list_box.set(-1, "[X] " + prev[4:])
self._list_box.add("[ ] " + act_name(act_number)) self._list_box.add("[ ] " + act_name(act_number))
self._list_box.select(-1)
self._render_list_box() self._render_list_box()
def _sync_position(self) -> None: def _sync_position(self) -> None:
@@ -48,6 +48,7 @@ class QuestBookWindow(Focusable, ListBoxProgressBarWindow):
self._list_box.clear() self._list_box.clear()
for quest_name in self._player.quest_book.quests[-self.cutoff :]: for quest_name in self._player.quest_book.quests[-self.cutoff :]:
self._sync_quest_add(quest_name) self._sync_quest_add(quest_name)
self._list_box.select(-1)
self._render_list_box() self._render_list_box()
def _sync_quest_add(self, quest_name: str) -> None: def _sync_quest_add(self, quest_name: str) -> None:
@@ -56,6 +57,7 @@ class QuestBookWindow(Focusable, ListBoxProgressBarWindow):
if prev is not None: if prev is not None:
self._list_box.set(-1, "[X] " + prev[4:]) self._list_box.set(-1, "[X] " + prev[4:])
self._list_box.add("[ ] " + quest_name) self._list_box.add("[ ] " + quest_name)
self._list_box.select(-1)
self._render_list_box() self._render_list_box()
def _sync_position(self) -> None: def _sync_position(self) -> None:
@@ -41,14 +41,17 @@ class SpellBookWindow(Focusable, WindowWrapper):
self._data_table.clear() self._data_table.clear()
for spell in self._player.spell_book: for spell in self._player.spell_book:
self._data_table.add(spell.name, to_roman(spell.level)) self._data_table.add(spell.name, to_roman(spell.level))
self._data_table.select(None)
self._render() self._render()
def _sync_spell_add(self, spell: Spell) -> None: def _sync_spell_add(self, spell: Spell) -> None:
self._data_table.add(spell.name, to_roman(spell.level)) self._data_table.add(spell.name, to_roman(spell.level))
self._data_table.select(spell.name)
self._render() self._render()
def _sync_spell_change(self, spell: Spell) -> None: def _sync_spell_change(self, spell: Spell) -> None:
self._data_table.set(spell.name, to_roman(spell.level)) self._data_table.set(spell.name, to_roman(spell.level))
self._data_table.select(spell.name)
self._render() self._render()
def _render(self) -> None: def _render(self) -> None:
+14
View File
@@ -2,6 +2,8 @@ import curses
import enum import enum
import typing as T import typing as T
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
from .scrollable import Scrollable from .scrollable import Scrollable
@@ -19,6 +21,7 @@ class DataTable(Scrollable):
self._align_right = align_right self._align_right = align_right
self._items: T.List[T.Tuple[str, str]] self._items: T.List[T.Tuple[str, str]]
self._selected: T.Optional[int] = None
def add(self, text: str, value: str) -> None: def add(self, text: str, value: str) -> None:
self._items.append((text, value)) self._items.append((text, value))
@@ -38,6 +41,9 @@ class DataTable(Scrollable):
del self._items[idx] del self._items[idx]
self.scroll_to_item(idx) self.scroll_to_item(idx)
def select(self, text: T.Optional[str]) -> None:
self._selected = None if text is None else self.get_idx(text)
def get_idx(self, text: str) -> T.Optional[int]: def get_idx(self, text: str) -> T.Optional[int]:
for idx, row in enumerate(self._items): for idx, row in enumerate(self._items):
if row[0].strip() == text.strip(): if row[0].strip() == text.strip():
@@ -48,20 +54,28 @@ class DataTable(Scrollable):
assert self._pad assert self._pad
if self._align_right: if self._align_right:
for y, row in enumerate(self._items): for y, row in enumerate(self._items):
if y == self._selected and has_colors():
self._pad.attron(curses.color_pair(COLOR_HIGHLIGHT))
self._pad.move(y, 0) self._pad.move(y, 0)
self._pad.addnstr(row[0], min(len(row[0]), w)) self._pad.addnstr(row[0], min(len(row[0]), w))
col2_x = max(0, w - len(row[1])) col2_x = max(0, w - len(row[1]))
if col2_x < w: if col2_x < w:
self._pad.move(y, col2_x) self._pad.move(y, col2_x)
self._pad.addstr(row[1]) self._pad.addstr(row[1])
if y == self._selected and has_colors():
self._pad.attroff(curses.color_pair(COLOR_HIGHLIGHT))
else: else:
col2_x = self._get_col_width(0) col2_x = self._get_col_width(0)
for y, row in enumerate(self._items): for y, row in enumerate(self._items):
if y == self._selected and has_colors():
self._pad.attron(curses.color_pair(COLOR_HIGHLIGHT))
self._pad.addnstr(y, 0, row[0], min(len(row[0]), w)) self._pad.addnstr(y, 0, row[0], min(len(row[0]), w))
if col2_x < w: if col2_x < w:
self._pad.addnstr( self._pad.addnstr(
y, col2_x, row[1], min(len(row[1]), w - col2_x) y, col2_x, row[1], min(len(row[1]), w - col2_x)
) )
if y == self._selected and has_colors():
self._pad.attroff(curses.color_pair(COLOR_HIGHLIGHT))
def _get_col_width(self, x: int) -> int: def _get_col_width(self, x: int) -> int:
return max([len(row[0]) for row in self._items] + [0]) return max([len(row[0]) for row in self._items] + [0])
+15
View File
@@ -2,12 +2,18 @@ import curses
import enum import enum
import typing as T import typing as T
from pqcli.ui.curses.colors import COLOR_HIGHLIGHT, has_colors
from .scrollable import Scrollable from .scrollable import Scrollable
class ListBox(Scrollable): class ListBox(Scrollable):
_items: T.List[str] _items: T.List[str]
def __init__(self, parent: T.Any, h: int, w: int, y: int, x: int) -> None:
super().__init__(parent, h, w, y, x)
self._selected: T.Optional[int] = None
def add(self, text: str) -> None: def add(self, text: str) -> None:
self._items.append(text) self._items.append(text)
self.scroll_to_item(-1) self.scroll_to_item(-1)
@@ -30,7 +36,16 @@ class ListBox(Scrollable):
def delete(self, idx: int, count: int = 1) -> None: def delete(self, idx: int, count: int = 1) -> None:
del self._items[idx : idx + count] del self._items[idx : idx + count]
def select(self, idx: T.Optional[int]) -> None:
if idx < 0:
idx %= len(self._items)
self._selected = idx
def _render_impl(self, h: int, w: int) -> None: def _render_impl(self, h: int, w: int) -> None:
assert self._pad assert self._pad
for y, item in enumerate(self._items): for y, item in enumerate(self._items):
if y == self._selected and has_colors():
self._pad.attron(curses.color_pair(COLOR_HIGHLIGHT))
self._pad.addnstr(y, 0, item, min(len(item), w)) self._pad.addnstr(y, 0, item, min(len(item), w))
if y == self._selected and has_colors():
self._pad.attroff(curses.color_pair(COLOR_HIGHLIGHT))