app: add slash commands
This commit is contained in:
@@ -115,6 +115,7 @@ class StatusBar(Static):
|
|||||||
connected = reactive(False)
|
connected = reactive(False)
|
||||||
reconnecting = reactive(False)
|
reconnecting = reactive(False)
|
||||||
self_deaf = reactive(False)
|
self_deaf = reactive(False)
|
||||||
|
self_mute = reactive(False)
|
||||||
server_info = reactive("")
|
server_info = reactive("")
|
||||||
output_vol = reactive(100)
|
output_vol = reactive(100)
|
||||||
input_vol = reactive(100)
|
input_vol = reactive(100)
|
||||||
@@ -148,11 +149,14 @@ class StatusBar(Static):
|
|||||||
|
|
||||||
deaf_sym = "[#f7768e]\u2298[/]" if self.self_deaf else ""
|
deaf_sym = "[#f7768e]\u2298[/]" if self.self_deaf else ""
|
||||||
deaf_full = "[#f7768e]\u2298[/] deaf" if self.self_deaf else ""
|
deaf_full = "[#f7768e]\u2298[/] deaf" if self.self_deaf else ""
|
||||||
|
mute_sym = "[#e0af68]\u2715[/]" if self.self_mute else ""
|
||||||
|
mute_full = "[#e0af68]\u2715[/] mute" if self.self_mute else ""
|
||||||
|
|
||||||
if w < 40:
|
if w < 40:
|
||||||
return f" {conn_sym} {deaf_sym}{ptt_sym}"
|
return f" {conn_sym} {deaf_sym}{mute_sym}{ptt_sym}"
|
||||||
if w < 60:
|
if w < 60:
|
||||||
return f" {conn_full} {deaf_full}{' ' if deaf_full else ''}{ptt_full}"
|
flags = f"{deaf_full}{' ' if deaf_full else ''}{mute_full}{' ' if mute_full else ''}"
|
||||||
|
return f" {conn_full} {flags}{ptt_full}"
|
||||||
|
|
||||||
vol = (
|
vol = (
|
||||||
f" [dim]out[/]{self._vol_bar(self.output_vol)}"
|
f" [dim]out[/]{self._vol_bar(self.output_vol)}"
|
||||||
@@ -164,8 +168,8 @@ class StatusBar(Static):
|
|||||||
else:
|
else:
|
||||||
pitch_str = ""
|
pitch_str = ""
|
||||||
info = f" [dim]{self.server_info}[/]" if self.server_info else ""
|
info = f" [dim]{self.server_info}[/]" if self.server_info else ""
|
||||||
deaf = f"{deaf_full} " if deaf_full else ""
|
flags = f"{deaf_full}{' ' if deaf_full else ''}{mute_full}{' ' if mute_full else ''}"
|
||||||
return f" {conn_full} {deaf}{ptt_full}{vol}{pitch_str}{info}"
|
return f" {conn_full} {flags}{ptt_full}{vol}{pitch_str}{info}"
|
||||||
|
|
||||||
|
|
||||||
class ChannelTree(Static):
|
class ChannelTree(Static):
|
||||||
@@ -450,6 +454,7 @@ class TuimbleApp(App):
|
|||||||
on_failure=self._reconnect_on_failure,
|
on_failure=self._reconnect_on_failure,
|
||||||
on_exhausted=self._reconnect_on_exhausted,
|
on_exhausted=self._reconnect_on_exhausted,
|
||||||
)
|
)
|
||||||
|
self._muted: bool = False
|
||||||
self._intentional_disconnect: bool = False
|
self._intentional_disconnect: bool = False
|
||||||
|
|
||||||
def _make_client(self, srv=None) -> MumbleClient:
|
def _make_client(self, srv=None) -> MumbleClient:
|
||||||
@@ -585,10 +590,12 @@ class TuimbleApp(App):
|
|||||||
|
|
||||||
def on_server_connected(self, _msg: ServerConnected) -> None:
|
def on_server_connected(self, _msg: ServerConnected) -> None:
|
||||||
self._intentional_disconnect = False
|
self._intentional_disconnect = False
|
||||||
|
self._muted = False
|
||||||
|
|
||||||
status = self.query_one("#status", StatusBar)
|
status = self.query_one("#status", StatusBar)
|
||||||
status.reconnecting = False
|
status.reconnecting = False
|
||||||
status.connected = True
|
status.connected = True
|
||||||
|
status.self_mute = False
|
||||||
srv = self._config.server
|
srv = self._config.server
|
||||||
status.server_info = f"{srv.host}:{srv.port}"
|
status.server_info = f"{srv.host}:{srv.port}"
|
||||||
|
|
||||||
@@ -651,6 +658,10 @@ class TuimbleApp(App):
|
|||||||
event.input.clear()
|
event.input.clear()
|
||||||
self._history.push(text)
|
self._history.push(text)
|
||||||
|
|
||||||
|
if text.startswith("/"):
|
||||||
|
self._dispatch_command(text)
|
||||||
|
return
|
||||||
|
|
||||||
if not self._client.connected:
|
if not self._client.connected:
|
||||||
self._show_error("not connected")
|
self._show_error("not connected")
|
||||||
return
|
return
|
||||||
@@ -659,6 +670,50 @@ class TuimbleApp(App):
|
|||||||
chatlog = self.query_one("#chatlog", ChatLog)
|
chatlog = self.query_one("#chatlog", ChatLog)
|
||||||
chatlog.write(f"[#e0af68]{self._config.server.username}[/] {text}")
|
chatlog.write(f"[#e0af68]{self._config.server.username}[/] {text}")
|
||||||
|
|
||||||
|
def _dispatch_command(self, text: str) -> None:
|
||||||
|
"""Handle slash commands."""
|
||||||
|
cmd = text.split()[0].lower()
|
||||||
|
chatlog = self.query_one("#chatlog", ChatLog)
|
||||||
|
|
||||||
|
if cmd == "/help":
|
||||||
|
chatlog.write("[dim]/deafen toggle self-deafen[/dim]")
|
||||||
|
chatlog.write("[dim]/mute toggle self-mute[/dim]")
|
||||||
|
chatlog.write("[dim]/unmute unmute yourself[/dim]")
|
||||||
|
chatlog.write("[dim]/register register on server[/dim]")
|
||||||
|
elif cmd == "/deafen":
|
||||||
|
self.action_toggle_deaf()
|
||||||
|
elif cmd == "/mute":
|
||||||
|
if self._muted:
|
||||||
|
chatlog.write("[dim]already muted[/dim]")
|
||||||
|
return
|
||||||
|
self._muted = True
|
||||||
|
self._audio.capturing = False
|
||||||
|
self._client.set_self_mute(True)
|
||||||
|
status = self.query_one("#status", StatusBar)
|
||||||
|
status.self_mute = True
|
||||||
|
status.ptt_active = False
|
||||||
|
chatlog.write("[#e0af68]\u2715 muted[/]")
|
||||||
|
elif cmd == "/unmute":
|
||||||
|
if not self._muted:
|
||||||
|
chatlog.write("[dim]already unmuted[/dim]")
|
||||||
|
return
|
||||||
|
self._muted = False
|
||||||
|
self._client.set_self_mute(False)
|
||||||
|
status = self.query_one("#status", StatusBar)
|
||||||
|
status.self_mute = False
|
||||||
|
chatlog.write("[#9ece6a]\u2713 unmuted[/]")
|
||||||
|
elif cmd == "/register":
|
||||||
|
if not self._client.connected:
|
||||||
|
self._show_error("not connected")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self._client.register_self()
|
||||||
|
chatlog.write("[#9ece6a]\u2713 registration requested[/]")
|
||||||
|
except Exception as exc:
|
||||||
|
self._show_error(f"register failed: {exc}")
|
||||||
|
else:
|
||||||
|
self._show_error(f"unknown command: {cmd}")
|
||||||
|
|
||||||
# -- audio ---------------------------------------------------------------
|
# -- audio ---------------------------------------------------------------
|
||||||
|
|
||||||
def _on_device_change(self) -> None:
|
def _on_device_change(self) -> None:
|
||||||
@@ -978,6 +1033,8 @@ class TuimbleApp(App):
|
|||||||
self._ptt.key_down()
|
self._ptt.key_down()
|
||||||
|
|
||||||
def _on_ptt_change(self, transmitting: bool) -> None:
|
def _on_ptt_change(self, transmitting: bool) -> None:
|
||||||
|
if self._muted:
|
||||||
|
transmitting = False
|
||||||
self._audio.capturing = transmitting
|
self._audio.capturing = transmitting
|
||||||
status = self.query_one("#status", StatusBar)
|
status = self.query_one("#status", StatusBar)
|
||||||
status.ptt_active = transmitting
|
status.ptt_active = transmitting
|
||||||
|
|||||||
@@ -256,6 +256,39 @@ async def test_statusbar_deaf():
|
|||||||
assert "\u2298" in rendered
|
assert "\u2298" in rendered
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_statusbar_muted():
|
||||||
|
app = StatusBarApp()
|
||||||
|
async with app.run_test(size=(80, 5)) as _pilot:
|
||||||
|
bar = app.query_one("#status", StatusBar)
|
||||||
|
bar.self_mute = True
|
||||||
|
rendered = bar.render()
|
||||||
|
assert "\u2715" in rendered
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_statusbar_muted_compact():
|
||||||
|
app = StatusBarApp()
|
||||||
|
async with app.run_test(size=(30, 5)) as pilot:
|
||||||
|
bar = app.query_one("#status", StatusBar)
|
||||||
|
bar.self_mute = True
|
||||||
|
await pilot.resize_terminal(30, 5)
|
||||||
|
await pilot.pause()
|
||||||
|
rendered = bar.render()
|
||||||
|
assert "\u2715" in rendered
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_statusbar_muted_medium():
|
||||||
|
app = StatusBarApp()
|
||||||
|
async with app.run_test(size=(50, 5)) as _pilot:
|
||||||
|
bar = app.query_one("#status", StatusBar)
|
||||||
|
bar.self_mute = True
|
||||||
|
rendered = bar.render()
|
||||||
|
assert "\u2715" in rendered
|
||||||
|
assert "mute" in rendered
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_statusbar_reconnecting():
|
async def test_statusbar_reconnecting():
|
||||||
app = StatusBarApp()
|
app = StatusBarApp()
|
||||||
|
|||||||
Reference in New Issue
Block a user