frobar-ng: Colors
This commit is contained in:
parent
7b54ad0578
commit
953aa13cb6
|
@ -5,18 +5,24 @@ import datetime
|
|||
import enum
|
||||
import ipaddress
|
||||
import logging
|
||||
import random
|
||||
import signal
|
||||
import socket
|
||||
import typing
|
||||
|
||||
import coloredlogs
|
||||
import i3ipc
|
||||
import i3ipc.aio
|
||||
import psutil
|
||||
import rich.color
|
||||
import rich.logging
|
||||
import rich.terminal_theme
|
||||
|
||||
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
|
||||
log = logging.getLogger()
|
||||
logging.basicConfig(
|
||||
level="DEBUG",
|
||||
format="%(message)s",
|
||||
datefmt="[%X]",
|
||||
handlers=[rich.logging.RichHandler()],
|
||||
)
|
||||
log = logging.getLogger("frobar")
|
||||
|
||||
T = typing.TypeVar("T", bound="ComposableText")
|
||||
P = typing.TypeVar("P", bound="ComposableText")
|
||||
|
@ -85,12 +91,6 @@ class ComposableText(typing.Generic[P, C]):
|
|||
return self.generateMarkup()
|
||||
|
||||
|
||||
def randomColor(seed: int | bytes | None = None) -> str:
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
return "#" + "".join(f"{random.randint(0, 0xff):02x}" for _ in range(3))
|
||||
|
||||
|
||||
class Button(enum.Enum):
|
||||
CLICK_LEFT = "1"
|
||||
CLICK_MIDDLE = "2"
|
||||
|
@ -104,11 +104,16 @@ class Section(ComposableText):
|
|||
Colorable block separated by chevrons
|
||||
"""
|
||||
|
||||
def __init__(self, parent: "Module", sortKey: Sortable = 0) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
parent: "Module",
|
||||
sortKey: Sortable = 0,
|
||||
color: rich.color.Color = rich.color.Color.default(),
|
||||
) -> None:
|
||||
super().__init__(parent=parent, sortKey=sortKey)
|
||||
self.parent: "Module"
|
||||
self.color = color
|
||||
|
||||
self.color = randomColor()
|
||||
self.desiredText: str | None = None
|
||||
self.text = ""
|
||||
self.targetSize = -1
|
||||
|
@ -224,6 +229,7 @@ class Side(ComposableText):
|
|||
self.children: typing.MutableSequence[Module] = []
|
||||
|
||||
self.alignment = alignment
|
||||
self.bar = parent.getFirstParentOfType(Bar)
|
||||
|
||||
def generateMarkup(self) -> str:
|
||||
if not self.children:
|
||||
|
@ -234,22 +240,23 @@ class Side(ComposableText):
|
|||
for section in module.getSections():
|
||||
if section.isHidden():
|
||||
continue
|
||||
hexa = section.color.get_truecolor(theme=self.bar.theme).hex
|
||||
if lastSection is None:
|
||||
if self.alignment == Alignment.LEFT:
|
||||
text += "%{B" + section.color + "}%{F-}"
|
||||
text += "%{B" + hexa + "}%{F-}"
|
||||
else:
|
||||
text += "%{B-}%{F" + section.color + "}%{R}%{F-}"
|
||||
text += "%{B-}%{F" + hexa + "}%{R}%{F-}"
|
||||
else:
|
||||
if self.alignment == Alignment.RIGHT:
|
||||
if lastSection.color == section.color:
|
||||
text += ""
|
||||
else:
|
||||
text += "%{F" + section.color + "}%{R}"
|
||||
text += "%{F" + hexa + "}%{R}"
|
||||
else:
|
||||
if lastSection.color == section.color:
|
||||
text += ""
|
||||
else:
|
||||
text += "%{R}%{B" + section.color + "}"
|
||||
text += "%{R}%{B" + hexa + "}"
|
||||
text += "%{F-}"
|
||||
text += section.getMarkup()
|
||||
lastSection = section
|
||||
|
@ -280,11 +287,15 @@ class Bar(ComposableText):
|
|||
Top-level
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
theme: rich.terminal_theme.TerminalTheme = rich.terminal_theme.DEFAULT_TERMINAL_THEME,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.parent: None
|
||||
self.children: typing.MutableSequence[Screen]
|
||||
self.longRunningTasks: list[asyncio.Task] = list()
|
||||
self.theme = theme
|
||||
|
||||
self.refresh = asyncio.Event()
|
||||
self.taskGroup = asyncio.TaskGroup()
|
||||
|
@ -312,6 +323,10 @@ class Bar(ComposableText):
|
|||
"64",
|
||||
"-f",
|
||||
"DejaVuSansM Nerd Font:size=10",
|
||||
"-F",
|
||||
self.theme.foreground_color.hex,
|
||||
"-B",
|
||||
self.theme.background_color.hex,
|
||||
]
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE
|
||||
|
@ -380,8 +395,9 @@ class Bar(ComposableText):
|
|||
|
||||
|
||||
class Provider:
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, color: rich.color.Color = rich.color.Color.default()) -> None:
|
||||
self.modules: list[Module] = list()
|
||||
self.color = color
|
||||
|
||||
async def run(self) -> None:
|
||||
# Not a NotImplementedError, otherwise can't combine all classes
|
||||
|
@ -389,8 +405,8 @@ class Provider:
|
|||
|
||||
|
||||
class MirrorProvider(Provider):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
def __init__(self, color: rich.color.Color = rich.color.Color.default()) -> None:
|
||||
super().__init__(color=color)
|
||||
self.module: Module
|
||||
|
||||
async def run(self) -> None:
|
||||
|
@ -403,11 +419,14 @@ class MirrorProvider(Provider):
|
|||
class SingleSectionProvider(MirrorProvider):
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.section = Section(parent=self.module)
|
||||
self.section = Section(parent=self.module, color=self.color)
|
||||
|
||||
|
||||
class StaticProvider(SingleSectionProvider):
|
||||
def __init__(self, text: str) -> None:
|
||||
def __init__(
|
||||
self, text: str, color: rich.color.Color = rich.color.Color.default()
|
||||
) -> None:
|
||||
super().__init__(color=color)
|
||||
self.text = text
|
||||
|
||||
async def run(self) -> None:
|
||||
|
@ -417,8 +436,13 @@ class StaticProvider(SingleSectionProvider):
|
|||
|
||||
class StatefulSection(Section):
|
||||
|
||||
def __init__(self, parent: Module, sortKey: Sortable = 0) -> None:
|
||||
super().__init__(parent=parent, sortKey=sortKey)
|
||||
def __init__(
|
||||
self,
|
||||
parent: Module,
|
||||
sortKey: Sortable = 0,
|
||||
color: rich.color.Color = rich.color.Color.default(),
|
||||
) -> None:
|
||||
super().__init__(parent=parent, sortKey=sortKey, color=color)
|
||||
self.state = 0
|
||||
self.numberStates: int
|
||||
|
||||
|
@ -444,7 +468,7 @@ class StatefulSection(Section):
|
|||
class SingleStatefulSectionProvider(MirrorProvider):
|
||||
async def run(self) -> None:
|
||||
await super().run()
|
||||
self.section = StatefulSection(parent=self.module)
|
||||
self.section = StatefulSection(parent=self.module, color=self.color)
|
||||
|
||||
|
||||
class PeriodicProvider(Provider):
|
||||
|
@ -518,8 +542,10 @@ class I3WindowTitleProvider(SingleSectionProvider):
|
|||
|
||||
|
||||
class I3WorkspacesProvider(Provider):
|
||||
# TODO Custom names
|
||||
# TODO Colors
|
||||
COLOR_URGENT = rich.color.Color.parse("red")
|
||||
COLOR_FOCUSED = rich.color.Color.parse("yellow")
|
||||
COLOR_VISIBLE = rich.color.Color.parse("blue")
|
||||
COLOR_DEFAULT = rich.color.Color.parse("bright_black")
|
||||
|
||||
async def updateWorkspaces(self, i3: i3ipc.Connection) -> None:
|
||||
"""
|
||||
|
@ -554,11 +580,15 @@ class I3WorkspacesProvider(Provider):
|
|||
)
|
||||
name = workspace.name
|
||||
if workspace.urgent:
|
||||
name = f"{name} !"
|
||||
section.color = self.COLOR_URGENT
|
||||
elif workspace.focused:
|
||||
name = f"{name} +"
|
||||
section.color = self.COLOR_FOCUSED
|
||||
elif workspace.visible:
|
||||
name = f"{name} *"
|
||||
section.color = self.COLOR_VISIBLE
|
||||
else:
|
||||
section.color = self.COLOR_DEFAULT
|
||||
if workspace.focused or workspace.visible:
|
||||
name = f"{name} X" # TODO Custom names
|
||||
section.setText(name)
|
||||
workspacesNums = set(workspace.num for workspace in workspaces)
|
||||
for num, section in self.sections.items():
|
||||
|
@ -595,8 +625,13 @@ class I3WorkspacesProvider(Provider):
|
|||
|
||||
|
||||
class NetworkProviderSection(StatefulSection):
|
||||
def __init__(self, parent: Module, iface: str, provider: "NetworkProvider") -> None:
|
||||
super().__init__(parent=parent, sortKey=iface)
|
||||
def __init__(
|
||||
self,
|
||||
parent: Module,
|
||||
iface: str,
|
||||
provider: "NetworkProvider",
|
||||
) -> None:
|
||||
super().__init__(parent=parent, sortKey=iface, color=provider.color)
|
||||
self.iface = iface
|
||||
self.provider = provider
|
||||
|
||||
|
@ -649,7 +684,7 @@ class NetworkProviderSection(StatefulSection):
|
|||
net = ipaddress.IPv4Network(
|
||||
(address.address, address.netmask), strict=False
|
||||
)
|
||||
text += f" {net.with_prefixlen}"
|
||||
text += f" {address.address}/{net.prefixlen}"
|
||||
break
|
||||
|
||||
if state >= 3: # Speed
|
||||
|
@ -670,8 +705,11 @@ class NetworkProviderSection(StatefulSection):
|
|||
|
||||
|
||||
class NetworkProvider(MirrorProvider, PeriodicProvider):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
def __init__(
|
||||
self,
|
||||
color: rich.color.Color = rich.color.Color.default(),
|
||||
) -> None:
|
||||
super().__init__(color=color)
|
||||
self.sections: dict[str, NetworkProviderSection] = dict()
|
||||
|
||||
async def init(self) -> None:
|
||||
|
@ -725,33 +763,86 @@ class TimeProvider(PeriodicStatefulProvider):
|
|||
|
||||
|
||||
async def main() -> None:
|
||||
bar = Bar()
|
||||
FROGARIZED = [
|
||||
"#092c0e",
|
||||
"#143718",
|
||||
"#5a7058",
|
||||
"#677d64",
|
||||
"#89947f",
|
||||
"#99a08d",
|
||||
"#fae2e3",
|
||||
"#fff0f1",
|
||||
"#e0332e",
|
||||
"#cf4b15",
|
||||
"#bb8801",
|
||||
"#8d9800",
|
||||
"#1fa198",
|
||||
"#008dd1",
|
||||
"#5c73c4",
|
||||
"#d43982",
|
||||
]
|
||||
# TODO Configurable
|
||||
|
||||
def base16_color(color: int) -> tuple[int, int, int]:
|
||||
hexa = FROGARIZED[color]
|
||||
return tuple(rich.color.parse_rgb_hex(hexa[1:]))
|
||||
|
||||
theme = rich.terminal_theme.TerminalTheme(
|
||||
base16_color(0x0),
|
||||
base16_color(0x0), # TODO should be 7, currently 0 so it's compatible with v2
|
||||
[
|
||||
base16_color(0x0), # black
|
||||
base16_color(0x8), # red
|
||||
base16_color(0xB), # green
|
||||
base16_color(0xA), # yellow
|
||||
base16_color(0xD), # blue
|
||||
base16_color(0xE), # magenta
|
||||
base16_color(0xC), # cyan
|
||||
base16_color(0x5), # white
|
||||
],
|
||||
[
|
||||
base16_color(0x3), # bright black
|
||||
base16_color(0x8), # bright red
|
||||
base16_color(0xB), # bright green
|
||||
base16_color(0xA), # bright yellow
|
||||
base16_color(0xD), # bright blue
|
||||
base16_color(0xE), # bright magenta
|
||||
base16_color(0xC), # bright cyan
|
||||
base16_color(0x7), # bright white
|
||||
],
|
||||
)
|
||||
|
||||
bar = Bar(theme=theme)
|
||||
dualScreen = len(bar.children) > 1
|
||||
|
||||
bar.addProvider(I3ModeProvider(), alignment=Alignment.LEFT)
|
||||
color = rich.color.Color.parse
|
||||
|
||||
bar.addProvider(I3ModeProvider(color=color("red")), alignment=Alignment.LEFT)
|
||||
bar.addProvider(I3WorkspacesProvider(), alignment=Alignment.LEFT)
|
||||
if dualScreen:
|
||||
bar.addProvider(
|
||||
I3WindowTitleProvider(), screenNum=0, alignment=Alignment.CENTER
|
||||
)
|
||||
bar.addProvider(
|
||||
StaticProvider(text="mpris"),
|
||||
StaticProvider(text="mpris", color=color("bright_white")),
|
||||
screenNum=1 if dualScreen else None,
|
||||
alignment=Alignment.CENTER,
|
||||
)
|
||||
|
||||
bar.addProvider(StaticProvider("C L M T B"), alignment=Alignment.RIGHT)
|
||||
bar.addProvider(
|
||||
StaticProvider("pulse"),
|
||||
StaticProvider("C L M T B", color=color("green")), alignment=Alignment.RIGHT
|
||||
)
|
||||
bar.addProvider(
|
||||
StaticProvider("pulse", color=color("magenta")),
|
||||
screenNum=1 if dualScreen else None,
|
||||
alignment=Alignment.RIGHT,
|
||||
)
|
||||
bar.addProvider(
|
||||
NetworkProvider(),
|
||||
NetworkProvider(color=color("blue")),
|
||||
screenNum=0 if dualScreen else None,
|
||||
alignment=Alignment.RIGHT,
|
||||
)
|
||||
bar.addProvider(TimeProvider(), alignment=Alignment.RIGHT)
|
||||
bar.addProvider(TimeProvider(color=color("cyan")), alignment=Alignment.RIGHT)
|
||||
|
||||
await bar.run()
|
||||
|
||||
|
|
|
@ -25,13 +25,14 @@ pkgs.python3Packages.buildPythonApplication rec {
|
|||
version = "2.0";
|
||||
|
||||
propagatedBuildInputs = with pkgs.python3Packages; [
|
||||
coloredlogs
|
||||
notmuch
|
||||
coloredlogs # old only
|
||||
i3ipc
|
||||
mpd2
|
||||
notmuch
|
||||
psutil
|
||||
pulsectl
|
||||
pyinotify
|
||||
rich
|
||||
];
|
||||
nativeBuildInputs =
|
||||
[ lemonbar ]
|
||||
|
|
Loading…
Reference in a new issue