From 0bfaf3070e17b54ba1de60977e95c967b49d3d35 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Tue, 28 Nov 2023 13:59:25 +0000 Subject: [PATCH] app focus --- src/textual_web/app_session.py | 4 +- src/textual_web/ganglion_client.py | 16 ++++ src/textual_web/packets.py | 114 ++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/src/textual_web/app_session.py b/src/textual_web/app_session.py index e7777f8..69fa26f 100644 --- a/src/textual_web/app_session.py +++ b/src/textual_web/app_session.py @@ -236,8 +236,8 @@ class AppSession(Session): await on_data(data) elif type_bytes == META: meta_data = json.loads(data) - if meta_data.get("type") == "exit": - await self.send_meta({"type": "exit"}) + if meta_data.get("type") in {"exit", "blur", "focus"}: + await self.send_meta({"type": meta_data["type"]}) else: await on_meta(json.loads(data)) diff --git a/src/textual_web/ganglion_client.py b/src/textual_web/ganglion_client.py index e724e71..bd1d238 100644 --- a/src/textual_web/ganglion_client.py +++ b/src/textual_web/ganglion_client.py @@ -17,6 +17,8 @@ from .environment import Environment from .exit_poller import ExitPoller from .identity import generate from .packets import ( + Blur, + Focus, PACKET_MAP, Handlers, NotifyTerminalSize, @@ -418,3 +420,17 @@ class GanglionClient(Handlers): async def on_route_ping(self, packet: RoutePing) -> None: await self.send(RoutePong(packet.route_key, packet.data)) + + async def on_focus(self, packet: Focus) -> None: + session_process = self.session_manager.get_session_by_route_key( + RouteKey(packet.route_key) + ) + if session_process is not None: + await session_process.send_meta({"type": "focus"}) + + async def on_blur(self, packet: Blur) -> None: + session_process = self.session_manager.get_session_by_route_key( + RouteKey(packet.route_key) + ) + if session_process is not None: + await session_process.send_meta({"type": "blur"}) diff --git a/src/textual_web/packets.py b/src/textual_web/packets.py index acb1915..75d01ff 100644 --- a/src/textual_web/packets.py +++ b/src/textual_web/packets.py @@ -1,7 +1,7 @@ """ This file is auto-generated from packets.yml and packets.py.template -Time: Sun Aug 27 07:38:29 2023 +Time: Tue Nov 28 13:57:53 2023 Version: 1 To regenerate run `make packets.py` (in src directory) @@ -67,6 +67,12 @@ class PacketType(IntEnum): # Notify the client that the terminal has change dimensions. NOTIFY_TERMINAL_SIZE = 11 # See NotifyTerminalSize() + # App has focus. + FOCUS = 12 # See Focus() + + # App was blurred. + BLUR = 13 # See Blur() + class Packet(tuple): """Base class for a packet. @@ -777,6 +783,100 @@ class NotifyTerminalSize(Packet): return self[3] +# PacketType.FOCUS (12) +class Focus(Packet): + """App has focus. + + Args: + route_key (str): Route key. + + """ + + sender: ClassVar[str] = "both" + """Permitted sender, should be "client", "server", or "both".""" + handler_name: ClassVar[str] = "on_focus" + """Name of the method used to handle this packet.""" + type: ClassVar[PacketType] = PacketType.FOCUS + """The packet type enumeration.""" + + _attributes: ClassVar[list[tuple[str, Type]]] = [ + ("route_key", str), + ] + _attribute_count = 1 + _get_handler = attrgetter("on_focus") + + def __new__(cls, route_key: str) -> "Focus": + return tuple.__new__(cls, (PacketType.FOCUS, route_key)) + + @classmethod + def build(cls, route_key: str) -> "Focus": + """Build and validate a packet from its attributes.""" + if not isinstance(route_key, str): + raise TypeError( + f'packets.Focus Type of "route_key" incorrect; expected str, found {type(route_key)}' + ) + return tuple.__new__(cls, (PacketType.FOCUS, route_key)) + + def __repr__(self) -> str: + _type, route_key = self + return f"Focus({abbreviate_repr(route_key)})" + + def __rich_repr__(self) -> rich.repr.Result: + yield "route_key", self.route_key + + @property + def route_key(self) -> str: + """Route key.""" + return self[1] + + +# PacketType.BLUR (13) +class Blur(Packet): + """App was blurred. + + Args: + route_key (str): Route key. + + """ + + sender: ClassVar[str] = "both" + """Permitted sender, should be "client", "server", or "both".""" + handler_name: ClassVar[str] = "on_blur" + """Name of the method used to handle this packet.""" + type: ClassVar[PacketType] = PacketType.BLUR + """The packet type enumeration.""" + + _attributes: ClassVar[list[tuple[str, Type]]] = [ + ("route_key", str), + ] + _attribute_count = 1 + _get_handler = attrgetter("on_blur") + + def __new__(cls, route_key: str) -> "Blur": + return tuple.__new__(cls, (PacketType.BLUR, route_key)) + + @classmethod + def build(cls, route_key: str) -> "Blur": + """Build and validate a packet from its attributes.""" + if not isinstance(route_key, str): + raise TypeError( + f'packets.Blur Type of "route_key" incorrect; expected str, found {type(route_key)}' + ) + return tuple.__new__(cls, (PacketType.BLUR, route_key)) + + def __repr__(self) -> str: + _type, route_key = self + return f"Blur({abbreviate_repr(route_key)})" + + def __rich_repr__(self) -> rich.repr.Result: + yield "route_key", self.route_key + + @property + def route_key(self) -> str: + """Route key.""" + return self[1] + + # A mapping of the packet id on to the packet class PACKET_MAP: dict[int, type[Packet]] = { 1: Ping, @@ -790,6 +890,8 @@ PACKET_MAP: dict[int, type[Packet]] = { 9: RoutePing, 10: RoutePong, 11: NotifyTerminalSize, + 12: Focus, + 13: Blur, } # A mapping of the packet name on to the packet class @@ -805,6 +907,8 @@ PACKET_NAME_MAP: dict[str, type[Packet]] = { "routeping": RoutePing, "routepong": RoutePong, "notifyterminalsize": NotifyTerminalSize, + "focus": Focus, + "blur": Blur, } @@ -865,6 +969,14 @@ class Handlers: """Notify the client that the terminal has change dimensions.""" await self.on_default(packet) + async def on_focus(self, packet: Focus) -> None: + """App has focus.""" + await self.on_default(packet) + + async def on_blur(self, packet: Blur) -> None: + """App was blurred.""" + await self.on_default(packet) + async def on_default(self, packet: Packet) -> None: """Called when a packet is not handled."""