Merge pull request #385 from ihabunek/colors-settings

Read TUI colors from settings
This commit is contained in:
Ivan Habunek 2023-07-25 09:10:01 +02:00 committed by GitHub
commit 5e8a7bb415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 84 deletions

View file

@ -50,3 +50,62 @@ sensitive = true
visibility = "unlisted" visibility = "unlisted"
scheduled_in = "30 minutes" scheduled_in = "30 minutes"
``` ```
## TUI color palette
TUI uses Urwid which provides several color modes. See
[Urwid documentation](https://urwid.org/manual/displayattributes.html)
for more details.
By default, TUI operates in 16-color mode which can be changed by setting the
`color` setting in the `[tui]` section to one of the following values:
* `1` (monochrome)
* `16` (default)
* `88`
* `256`
* `16777216` (24 bit)
TUI defines a list of colors which can be customized, currently they can be seen
[in the source code](https://github.com/ihabunek/toot/blob/master/toot/tui/constants.py). They can be overriden in the `[tui.palette]` section.
Each color is defined as a list of upto 5 values:
* foreground color (16 color mode)
* background color (16 color mode)
* monochrome color (monochrome mode)
* foreground color (high-color mode)
* background color (high-color mode)
Any colors which are not used by your desired color mode can be skipped or set
to an empty string.
For example, to change the button colors in 16 color mode:
```toml
[tui.palette]
button = ["dark red,bold", ""]
button_focused = ["light gray", "green"]
```
In monochrome mode:
```toml
[tui]
colors = 1
[tui.palette]
button = ["", "", "bold"]
button_focused = ["", "", "italics"]
```
In 256 color mode:
```toml
[tui]
colors = 256
[tui.palette]
button = ["", "", "", "#aaa", "#bbb"]
button_focused = ["", "", "", "#aaa", "#bbb"]
```

View file

@ -1,15 +0,0 @@
from toot.tui.constants import PALETTE, MONO_PALETTE
def test_palette():
# for every entry in PALETTE, there must be
# a corresponding entry in MONO_PALETTE
for pal in PALETTE:
matches = [item for item in MONO_PALETTE if item[0] == pal[0]]
assert len(matches) > 0, f"{pal}, present in PALETTE, missing from MONO_PALETTE"
# for every entry in MONO_PALETTE, there must be
# a corresponding entry in PALETTE
for pal in MONO_PALETTE:
matches = [item for item in PALETTE if item[0] == pal[0]]
assert len(matches) > 0, f"{pal}, present in MONO_PALETTE, missing from PALETTE"

View file

@ -3,12 +3,12 @@ import urwid
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from toot import api, config, __version__ from toot import api, config, __version__, settings
from toot.console import get_default_visibility from toot.console import get_default_visibility
from toot.exceptions import ApiError from toot.exceptions import ApiError
from .compose import StatusComposer from .compose import StatusComposer
from .constants import PALETTE, MONO_PALETTE from .constants import PALETTE
from .entities import Status from .entities import Status
from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusLinks, StatusZoom from .overlays import ExceptionStackTrace, GotoMenu, Help, StatusSource, StatusLinks, StatusZoom
from .overlays import StatusDeleteConfirmation, Account from .overlays import StatusDeleteConfirmation, Account
@ -78,19 +78,20 @@ class TUI(urwid.Frame):
loop: urwid.MainLoop loop: urwid.MainLoop
screen: urwid.BaseScreen screen: urwid.BaseScreen
@classmethod @staticmethod
def create(cls, app, user, args): def create(app, user, args):
"""Factory method, sets up TUI and an event loop.""" """Factory method, sets up TUI and an event loop."""
screen = urwid.raw_display.Screen() screen = TUI.create_screen(args)
tui = cls(app, user, screen, args) tui = TUI(app, user, screen, args)
if args.no_color: palette = PALETTE.copy()
screen.set_terminal_properties(1) overrides = settings.get_setting("tui.palette", dict, {})
screen.reset_default_terminal_palette() for name, styles in overrides.items():
palette.append(tuple([name] + styles))
loop = urwid.MainLoop( loop = urwid.MainLoop(
tui, tui,
palette=MONO_PALETTE if args.no_color else PALETTE, palette=palette,
event_loop=urwid.AsyncioEventLoop(), event_loop=urwid.AsyncioEventLoop(),
unhandled_input=tui.unhandled_input, unhandled_input=tui.unhandled_input,
screen=screen, screen=screen,
@ -99,6 +100,18 @@ class TUI(urwid.Frame):
return tui return tui
@staticmethod
def create_screen(args):
screen = urwid.raw_display.Screen()
# Determine how many colors to use
default_colors = 1 if args.no_color else 16
colors = settings.get_setting("tui.colors", int, default_colors)
logger.debug(f"Setting colors to {colors}")
screen.set_terminal_properties(colors)
return screen
def __init__(self, app, user, screen, args): def __init__(self, app, user, screen, args):
self.app = app self.app = app
self.user = user self.user = user

View file

@ -1,8 +1,19 @@
# name, fg, bg, mono, fg_h, bg_h # Color definitions are tuples of:
# - name
# - foreground (normal mode)
# - background (normal mode)
# - foreground (monochrome mode)
# - foreground (high color mode)
# - background (high color mode)
#
# See:
# http://urwid.org/tutorial/index.html#display-attributes
# http://urwid.org/manual/displayattributes.html#using-display-attributes
PALETTE = [ PALETTE = [
# Components # Components
('button', 'white', 'black'), ('button', 'white', 'black'),
('button_focused', 'light gray', 'dark magenta'), ('button_focused', 'light gray', 'dark magenta', 'bold,underline'),
('card_author', 'yellow', ''), ('card_author', 'yellow', ''),
('card_title', 'dark green', ''), ('card_title', 'dark green', ''),
('columns_divider', 'white', 'dark blue'), ('columns_divider', 'white', 'dark blue'),
@ -14,7 +25,7 @@ PALETTE = [
('footer_status', 'white', 'dark blue'), ('footer_status', 'white', 'dark blue'),
('footer_status_bold', 'white, bold', 'dark blue'), ('footer_status_bold', 'white, bold', 'dark blue'),
('header', 'white', 'dark blue'), ('header', 'white', 'dark blue'),
('header_bold', 'white,bold', 'dark blue'), ('header_bold', 'white,bold', 'dark blue', 'bold'),
('intro_bigtext', 'yellow', ''), ('intro_bigtext', 'yellow', ''),
('intro_smalltext', 'light blue', ''), ('intro_smalltext', 'light blue', ''),
('poll_bar', 'white', 'dark blue'), ('poll_bar', 'white', 'dark blue'),
@ -22,16 +33,17 @@ PALETTE = [
('status_detail_bookmarked', 'light red', ''), ('status_detail_bookmarked', 'light red', ''),
('status_detail_timestamp', 'light blue', ''), ('status_detail_timestamp', 'light blue', ''),
('status_list_account', 'dark green', ''), ('status_list_account', 'dark green', ''),
('status_list_selected', 'white,bold', 'dark green'), ('status_list_selected', 'white,bold', 'dark green', 'bold,underline'),
('status_list_timestamp', 'light blue', ''), ('status_list_timestamp', 'light blue', ''),
# Functional # Functional
('hashtag', 'light cyan,bold', ''), ('account', 'dark green', ''),
('hashtag_followed', 'yellow,bold', ''), ('hashtag', 'light cyan,bold', '', 'bold'),
('link', ',italics', ''), ('hashtag_followed', 'yellow,bold', '', 'bold'),
('link_focused', ',italics', 'dark magenta'), ('link', ',italics', '', ',italics'),
('link_focused', ',italics', 'dark magenta', "underline,italics"),
('shortcut', 'light blue', ''), ('shortcut', 'light blue', ''),
('shortcut_highlight', 'white,bold', ''), ('shortcut_highlight', 'white,bold', '', 'bold'),
('warning', 'light red', ''), ('warning', 'light red', ''),
# Visiblity # Visiblity
@ -47,54 +59,6 @@ PALETTE = [
('success', 'dark green', ''), ('success', 'dark green', ''),
] ]
MONO_PALETTE = [
# Components
('button', 'white', 'black'),
('button_focused', 'black', 'white'),
('card_author', 'white', ''),
('card_title', 'white, bold', ''),
('columns_divider', 'white', 'black'),
('content_warning', 'white', 'black'),
('editbox', 'white', 'black'),
('editbox_focused', 'black', 'white'),
('footer_message', 'white', 'black'),
('footer_message_error', 'white,bold', 'black'),
('footer_status', 'black', 'white'),
('footer_status_bold', 'black,bold', 'white'),
('header', 'black', 'white'),
('header_bold', 'black,bold', 'white'),
('intro_bigtext', 'white', 'black'),
('intro_smalltext', 'white', 'black'),
('poll_bar', 'black', 'white'),
('status_detail_account', 'white', ''),
('status_detail_bookmarked', 'white', ''),
('status_detail_timestamp', 'white', ''),
('status_list_account', 'white', ''),
('status_list_selected', 'white,bold', ''),
('status_list_timestamp', 'white', ''),
('warning', 'white,bold', 'black'),
# Functional
('hashtag_followed', 'white,bold', ''),
('hashtag', 'white,bold', ''),
('link', ',italics', ''),
('link_focused', ',bold,italics', ''),
('shortcut', 'white', ''),
('shortcut_highlight', 'white,bold', ''),
# Visiblity
('visibility_public', 'white', ''),
('visibility_unlisted', 'white', ''),
('visibility_private', 'white', ''),
('visibility_direct', 'white', ''),
# Styles
('bold', ',bold', ''),
('dim', 'light gray', ''),
('highlight', ',bold', ''),
('success', '', ''),
]
VISIBILITY_OPTIONS = [ VISIBILITY_OPTIONS = [
("public", "Public", "Post to public timelines"), ("public", "Public", "Post to public timelines"),
("unlisted", "Unlisted", "Do not post to public timelines"), ("unlisted", "Unlisted", "Do not post to public timelines"),

View file

@ -326,9 +326,9 @@ class StatusDetails(urwid.Pile):
yield ("pack", urwid.AttrMap(urwid.Divider("-"), "dim")) yield ("pack", urwid.AttrMap(urwid.Divider("-"), "dim"))
if status.author.display_name: if status.author.display_name:
yield ("pack", urwid.Text(("status_detail_author", status.author.display_name))) yield ("pack", urwid.Text(("bold", status.author.display_name)))
account_color = "highlight" if status.author.account in self.followed_accounts else "dim" account_color = "highlight" if status.author.account in self.followed_accounts else "account"
yield ("pack", urwid.Text((account_color, status.author.account))) yield ("pack", urwid.Text((account_color, status.author.account)))
yield ("pack", urwid.Divider()) yield ("pack", urwid.Divider())