ui/curses: add colors

This commit is contained in:
rr-
2019-01-13 17:33:11 +01:00
parent 7a835c615e
commit 9fc078ad94
19 changed files with 137 additions and 42 deletions
+7
View File
@@ -38,6 +38,13 @@ def parse_args() -> argparse.Namespace:
help="Use curses user interface (slow, but rich colors output)", help="Use curses user interface (slow, but rich colors output)",
) )
parser.add_argument(
"--no-colors",
dest="colors",
action="store_false",
help="Disable color highlighting in curses interface",
)
parser.add_argument( parser.add_argument(
"--no-config", "--no-config",
dest="use_config", dest="use_config",
+19
View File
@@ -9,6 +9,15 @@ from pqcli import lingo
from pqcli.config import Class, Race from pqcli.config import Class, Race
from pqcli.mechanic import Player, Stats, create_player from pqcli.mechanic import Player, Stats, create_player
from pqcli.roster import Roster from pqcli.roster import Roster
from pqcli.ui.curses.colors import (
COLOR_FOCUSED,
COLOR_LOGO,
COLOR_PROGRESSBAR,
COLOR_SCROLLBAR_THUMB,
COLOR_SCROLLBAR_TRACK,
has_colors,
set_colors,
)
from pqcli.ui.curses.views import ( from pqcli.ui.curses.views import (
BaseView, BaseView,
ChooseCharacterClassView, ChooseCharacterClassView,
@@ -38,6 +47,16 @@ class CursesUserInterface(BaseUserInterface):
curses.noecho() curses.noecho()
curses.curs_set(0) curses.curs_set(0)
set_colors(args.colors)
if has_colors():
curses.start_color()
curses.use_default_colors()
curses.init_pair(COLOR_LOGO, 10, -1)
curses.init_pair(COLOR_FOCUSED, -1, 8)
curses.init_pair(COLOR_SCROLLBAR_THUMB, -1, 0)
curses.init_pair(COLOR_SCROLLBAR_TRACK, -1, -1)
curses.init_pair(COLOR_PROGRESSBAR, 0, 9)
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:
self.roster.save() self.roster.save()
+16
View File
@@ -0,0 +1,16 @@
_HAS_COLORS = False
COLOR_LOGO = 1
COLOR_FOCUSED = 2
COLOR_SCROLLBAR_TRACK = 3
COLOR_SCROLLBAR_THUMB = 4
COLOR_PROGRESSBAR = 5
def has_colors() -> bool:
return _HAS_COLORS
def set_colors(has_colors: bool) -> None:
global _HAS_COLORS
_HAS_COLORS = has_colors
+1 -2
View File
@@ -8,11 +8,10 @@ from pqcli.roster import Roster
from pqcli.ui.curses.event_handler import EventHandler from pqcli.ui.curses.event_handler import EventHandler
from pqcli.ui.curses.util import KEYS_DOWN, KEYS_LEFT, KEYS_RIGHT, KEYS_UP from pqcli.ui.curses.util import KEYS_DOWN, KEYS_LEFT, KEYS_RIGHT, KEYS_UP
from pqcli.ui.curses.views.base_view import BaseView from pqcli.ui.curses.views.base_view import BaseView
from pqcli.ui.curses.widgets import Scrollable, Widget from pqcli.ui.curses.widgets import Focusable, Scrollable, Widget
from .character_sheet_window import CharacterSheetWindow from .character_sheet_window import CharacterSheetWindow
from .equipment_window import EquipmentWindow from .equipment_window import EquipmentWindow
from .focusable import Focusable
from .inventory_window import InventoryWindow from .inventory_window import InventoryWindow
from .plot_window import PlotWindow from .plot_window import PlotWindow
from .quest_book_window import QuestBookWindow from .quest_book_window import QuestBookWindow
@@ -1,8 +1,8 @@
import typing as T import typing as T
from pqcli.mechanic import Player, StatType from pqcli.mechanic import Player, StatType
from pqcli.ui.curses.widgets import Focusable
from .focusable import Focusable
from .progress_bar_window import DataTableProgressBarWindow from .progress_bar_window import DataTableProgressBarWindow
@@ -1,9 +1,7 @@
import typing as T import typing as T
from pqcli.mechanic import EquipmentType, Player from pqcli.mechanic import EquipmentType, Player
from pqcli.ui.curses.widgets import DataTable, WindowWrapper from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
from .focusable import Focusable
class EquipmentWindow(Focusable, WindowWrapper): class EquipmentWindow(Focusable, WindowWrapper):
@@ -3,9 +3,13 @@ import typing as T
from pqcli.lingo import to_roman from pqcli.lingo import to_roman
from pqcli.mechanic import InventoryItem, Player from pqcli.mechanic import InventoryItem, Player
from pqcli.ui.curses.widgets import DataTable, ProgressBar, WindowWrapper from pqcli.ui.curses.widgets import (
DataTable,
Focusable,
ProgressBar,
WindowWrapper,
)
from .focusable import Focusable
from .progress_bar_window import DataTableProgressBarWindow from .progress_bar_window import DataTableProgressBarWindow
@@ -2,9 +2,8 @@ import typing as T
from pqcli.lingo import act_name from pqcli.lingo import act_name
from pqcli.mechanic import Player from pqcli.mechanic import Player
from pqcli.ui.curses.widgets import ListBox, WindowWrapper from pqcli.ui.curses.widgets import Focusable, ListBox, WindowWrapper
from .focusable import Focusable
from .progress_bar_window import ListBoxProgressBarWindow from .progress_bar_window import ListBoxProgressBarWindow
@@ -1,9 +1,9 @@
import curses import curses
import typing as T import typing as T
from pqcli.ui.curses.views.game_view.focusable import Focusable
from pqcli.ui.curses.widgets import ( from pqcli.ui.curses.widgets import (
DataTable, DataTable,
Focusable,
ListBox, ListBox,
ProgressBar, ProgressBar,
WindowWrapper, WindowWrapper,
@@ -1,9 +1,8 @@
import typing as T import typing as T
from pqcli.mechanic import Player from pqcli.mechanic import Player
from pqcli.ui.curses.widgets import ListBox, WindowWrapper from pqcli.ui.curses.widgets import Focusable, ListBox, WindowWrapper
from .focusable import Focusable
from .progress_bar_window import ListBoxProgressBarWindow from .progress_bar_window import ListBoxProgressBarWindow
@@ -2,9 +2,7 @@ import typing as T
from pqcli.lingo import to_roman from pqcli.lingo import to_roman
from pqcli.mechanic import Player, Spell from pqcli.mechanic import Player, Spell
from pqcli.ui.curses.widgets import DataTable, WindowWrapper from pqcli.ui.curses.widgets import DataTable, Focusable, WindowWrapper
from .focusable import Focusable
class SpellBookWindow(Focusable, WindowWrapper): class SpellBookWindow(Focusable, WindowWrapper):
@@ -1,9 +1,12 @@
import typing as T import typing as T
from pqcli.mechanic import Player from pqcli.mechanic import Player
from pqcli.ui.curses.widgets import Label, ProgressBar, WindowWrapper from pqcli.ui.curses.widgets import (
Focusable,
from .focusable import Focusable Label,
ProgressBar,
WindowWrapper,
)
class TaskProgressWindow(Focusable, WindowWrapper): class TaskProgressWindow(Focusable, WindowWrapper):
+24 -9
View File
@@ -1,8 +1,11 @@
import curses
import typing as T import typing as T
from ..event_handler import EventHandler from pqcli.ui.curses.colors import COLOR_LOGO, has_colors
from ..util import Choice from pqcli.ui.curses.event_handler import EventHandler
from ..widgets import Menu from pqcli.ui.curses.util import Choice
from pqcli.ui.curses.widgets import Menu
from .base_view import BaseView from .base_view import BaseView
LOGO = """ LOGO = """
@@ -13,6 +16,22 @@ LOGO = """
""".strip() """.strip()
class MainMenu(Menu):
def __init__(
self, choices: T.List[Choice], scr_height: int, scr_width: int
) -> None:
super().__init__(
header=LOGO,
choices=choices,
active_choice=0,
scr_height=scr_height,
scr_width=scr_width,
)
if has_colors():
for y in range(len(self._header_lines)):
self._pad.chgat(y, 0, curses.color_pair(COLOR_LOGO))
class RosterView(BaseView): class RosterView(BaseView):
def __init__(self, screen: T.Any) -> None: def __init__(self, screen: T.Any) -> None:
super().__init__(screen) super().__init__(screen)
@@ -50,12 +69,8 @@ class RosterView(BaseView):
self.screen.erase() self.screen.erase()
self.screen.noutrefresh() self.screen.noutrefresh()
self.main_menu = Menu( self.main_menu = MainMenu(
header=LOGO, choices=self.choices, scr_height=scr_height, scr_width=scr_width
choices=self.choices,
active_choice=0,
scr_height=scr_height,
scr_width=scr_width,
) )
self.main_menu.render() self.main_menu.render()
+1
View File
@@ -1,5 +1,6 @@
from .base import Widget, WindowWrapper from .base import Widget, WindowWrapper
from .data_table import DataTable from .data_table import DataTable
from .focusable import Focusable
from .label import Label from .label import Label
from .list_box import ListBox from .list_box import ListBox
from .menu import Menu from .menu import Menu
@@ -1,9 +1,26 @@
import contextlib import contextlib
import curses
import typing as T import typing as T
from pqcli.ui.curses.colors import COLOR_FOCUSED, has_colors
from pqcli.ui.curses.event_handler import EventHandler from pqcli.ui.curses.event_handler import EventHandler
@contextlib.contextmanager
def focus_standout(focused: bool, win: T.Any) -> T.Generator:
if focused:
if has_colors():
win.attron(curses.color_pair(COLOR_FOCUSED))
else:
win.standout()
yield
if focused:
if has_colors():
win.attroff(curses.color_pair(COLOR_FOCUSED))
else:
win.standend()
class Focusable: class Focusable:
def __init__(self, *args: T.Any, **kwargs: T.Any) -> None: def __init__(self, *args: T.Any, **kwargs: T.Any) -> None:
self._focused = False self._focused = False
@@ -21,8 +38,5 @@ class Focusable:
@contextlib.contextmanager @contextlib.contextmanager
def focus_standout(self, win: T.Any) -> T.Generator: def focus_standout(self, win: T.Any) -> T.Generator:
if self._focused: with focus_standout(self._focused, win):
win.standout()
yield yield
if self._focused:
win.standend()
+2 -4
View File
@@ -3,6 +3,7 @@ import curses.ascii
import typing as T import typing as T
from pqcli.ui.curses.util import KEYS_DOWN, KEYS_UP from pqcli.ui.curses.util import KEYS_DOWN, KEYS_UP
from pqcli.ui.curses.widgets.focusable import focus_standout
from ..util import Choice from ..util import Choice
from .base import Widget from .base import Widget
@@ -73,11 +74,8 @@ class Menu(Widget):
for y, choice in enumerate(self._choices): for y, choice in enumerate(self._choices):
self._pad.move(len(self._header_lines) + 1 + y, 0) self._pad.move(len(self._header_lines) + 1 + y, 0)
if y == self._active_choice: with focus_standout(y == self._active_choice, self._pad):
self._pad.standout()
self._pad.addstr(choice.desc) self._pad.addstr(choice.desc)
if y == self._active_choice:
self._pad.standend()
self._pad.refresh( self._pad.refresh(
0, 0,
+8 -1
View File
@@ -3,6 +3,7 @@ import datetime
import typing as T import typing as T
from pqcli.lingo import format_timespan from pqcli.lingo import format_timespan
from pqcli.ui.curses.colors import COLOR_PROGRESSBAR, has_colors
from pqcli.ui.curses.widgets.base import WindowWrapper from pqcli.ui.curses.widgets.base import WindowWrapper
@@ -47,7 +48,13 @@ class ProgressBar(WindowWrapper):
self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1] - 1)) self._win.addnstr(0, x, text, min(len(text), self.getmaxyx()[1] - 1))
x = int(cur_pos * self.getmaxyx()[1] // max_pos) x = int(cur_pos * self.getmaxyx()[1] // max_pos)
if x > 0: if x > 0:
self._win.chgat(0, 0, curses.A_REVERSE) self._win.chgat(
0,
0,
curses.color_pair(COLOR_PROGRESSBAR)
if has_colors()
else curses.A_REVERSE,
)
if x < self.getmaxyx()[1]: if x < self.getmaxyx()[1]:
self._win.chgat(0, x, curses.A_NORMAL) self._win.chgat(0, x, curses.A_NORMAL)
self._win.noutrefresh() self._win.noutrefresh()
+20 -2
View File
@@ -1,6 +1,12 @@
import curses import curses
import typing as T import typing as T
from pqcli.ui.curses.colors import (
COLOR_SCROLLBAR_THUMB,
COLOR_SCROLLBAR_TRACK,
has_colors,
)
from .base import WindowWrapper from .base import WindowWrapper
@@ -69,13 +75,25 @@ class Scrollable(WindowWrapper):
y2 = self._scroll_pos + h y2 = self._scroll_pos + h
thumb_y1 = int(y1 * h // len(self._items)) thumb_y1 = int(y1 * h // len(self._items))
thumb_y2 = int(y2 * h // len(self._items)) thumb_y2 = int(y2 * h // len(self._items))
attr_thumb = (
curses.color_pair(COLOR_SCROLLBAR_THUMB)
if has_colors()
else curses.A_REVERSE
)
attr_track = (
curses.color_pair(COLOR_SCROLLBAR_TRACK)
if has_colors()
else curses.A_NORMAL
)
for win_y in range(h): for win_y in range(h):
self._win.chgat( self._win.chgat(
win_y, win_y,
w - 1, w - 1,
curses.A_REVERSE attr_thumb
if thumb_y1 <= win_y <= thumb_y2 if thumb_y1 <= win_y <= thumb_y2
else curses.A_NORMAL, else attr_track,
) )
self._win.noutrefresh() self._win.noutrefresh()
+1 -1
View File
@@ -5,4 +5,4 @@ from .data_table import DataTable
from .double_line_box import DoubleLineBox from .double_line_box import DoubleLineBox
from .layout import NColumns, NPile from .layout import NColumns, NPile
from .read_only_check_box import ReadOnlyCheckBox from .read_only_check_box import ReadOnlyCheckBox
from .scrollable import ScrollBar, Scrollable from .scrollable import Scrollable, ScrollBar