app: add volume key bindings and status display
This commit is contained in:
@@ -20,6 +20,16 @@ from tuimble.ptt import KittyPtt, TogglePtt, detect_backend
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
VOLUME_STEPS = (0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0)
|
||||
|
||||
|
||||
def _next_volume(current: float) -> float:
|
||||
"""Cycle through VOLUME_STEPS, wrapping to 0.0 after max."""
|
||||
for step in VOLUME_STEPS:
|
||||
if step > current + 0.01:
|
||||
return step
|
||||
return VOLUME_STEPS[0]
|
||||
|
||||
|
||||
# -- custom messages (pymumble thread -> Textual) ----------------------------
|
||||
|
||||
@@ -63,6 +73,14 @@ class StatusBar(Static):
|
||||
connected = reactive(False)
|
||||
self_deaf = reactive(False)
|
||||
server_info = reactive("")
|
||||
output_vol = reactive(100)
|
||||
input_vol = reactive(100)
|
||||
|
||||
@staticmethod
|
||||
def _vol_bar(pct: int) -> str:
|
||||
"""Compact 4-char volume indicator using block chars."""
|
||||
filled = round(pct / 25)
|
||||
return "\u2588" * filled + "\u2591" * (4 - filled)
|
||||
|
||||
def render(self) -> str:
|
||||
w = self.content_size.width if self.content_size.width > 0 else 80
|
||||
@@ -88,8 +106,14 @@ class StatusBar(Static):
|
||||
return f" {conn_sym} {deaf_sym}{ptt_sym}"
|
||||
if w < 60:
|
||||
return f" {conn_full} {deaf_full}{' ' if deaf_full else ''}{ptt_full}"
|
||||
|
||||
vol = (
|
||||
f" [dim]out[/]{self._vol_bar(self.output_vol)}"
|
||||
f" [dim]in[/]{self._vol_bar(self.input_vol)}"
|
||||
)
|
||||
info = f" [dim]{self.server_info}[/]" if self.server_info else ""
|
||||
return f" {conn_full} {deaf_full}{' ' if deaf_full else ''}{ptt_full}{info}"
|
||||
deaf = f"{deaf_full} " if deaf_full else ""
|
||||
return f" {conn_full} {deaf}{ptt_full}{vol}{info}"
|
||||
|
||||
|
||||
class ChannelTree(Static):
|
||||
@@ -345,6 +369,8 @@ class TuimbleApp(App):
|
||||
|
||||
BINDINGS = [
|
||||
("f1", "toggle_deaf", "Deafen"),
|
||||
("f2", "cycle_output_volume", "Vol Out"),
|
||||
("f3", "cycle_input_volume", "Vol In"),
|
||||
("q", "quit", "Quit"),
|
||||
("ctrl+c", "quit", "Quit"),
|
||||
]
|
||||
@@ -388,6 +414,10 @@ class TuimbleApp(App):
|
||||
yield Footer()
|
||||
|
||||
def on_mount(self) -> None:
|
||||
status = self.query_one("#status", StatusBar)
|
||||
status.output_vol = int(self._audio.output_gain * 100)
|
||||
status.input_vol = int(self._audio.input_gain * 100)
|
||||
|
||||
chatlog = self.query_one("#chatlog", ChatLog)
|
||||
chatlog.write("[dim]tuimble v0.1.0[/dim]")
|
||||
srv = self._config.server
|
||||
@@ -550,6 +580,28 @@ class TuimbleApp(App):
|
||||
else:
|
||||
chatlog.write("[#9ece6a]\u2713 undeafened[/]")
|
||||
|
||||
# -- volume ---------------------------------------------------------------
|
||||
|
||||
def action_cycle_output_volume(self) -> None:
|
||||
"""Cycle output volume through preset steps."""
|
||||
vol = _next_volume(self._audio.output_gain)
|
||||
self._audio.output_gain = vol
|
||||
pct = int(vol * 100)
|
||||
status = self.query_one("#status", StatusBar)
|
||||
status.output_vol = pct
|
||||
chatlog = self.query_one("#chatlog", ChatLog)
|
||||
chatlog.write(f"[dim]output volume {pct}%[/dim]")
|
||||
|
||||
def action_cycle_input_volume(self) -> None:
|
||||
"""Cycle input volume through preset steps."""
|
||||
vol = _next_volume(self._audio.input_gain)
|
||||
self._audio.input_gain = vol
|
||||
pct = int(vol * 100)
|
||||
status = self.query_one("#status", StatusBar)
|
||||
status.input_vol = pct
|
||||
chatlog = self.query_one("#chatlog", ChatLog)
|
||||
chatlog.write(f"[dim]input volume {pct}%[/dim]")
|
||||
|
||||
# -- PTT -----------------------------------------------------------------
|
||||
|
||||
def on_key(self, event: events.Key) -> None:
|
||||
|
||||
Reference in New Issue
Block a user