From 151b6f9e10209ec0185063a1e395690c79428cab Mon Sep 17 00:00:00 2001 From: David Brochart Date: Wed, 21 Feb 2024 11:57:41 +0100 Subject: [PATCH] Add file dirty indicator --- plugins/jpterm/txl_jpterm/main_area.py | 30 ++++++++++++++--- .../txl_notebook_editor/components.py | 9 ++++++ .../text_editor/txl_text_editor/components.py | 32 +++++++++++++++++-- txl/pyproject.toml | 2 +- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/plugins/jpterm/txl_jpterm/main_area.py b/plugins/jpterm/txl_jpterm/main_area.py index 06e6e16..f55098e 100644 --- a/plugins/jpterm/txl_jpterm/main_area.py +++ b/plugins/jpterm/txl_jpterm/main_area.py @@ -31,19 +31,39 @@ def show(self, widget: Widget, title: Optional[str] = None): else: self.tabs.add_tab(tab) self.tabs.active = tab.id - self.widgets[tab.id] = widget + self.widgets[tab] = widget self.mounted.append(widget) self.mount(widget) else: - tab_id = list(self.widgets.keys())[list(self.widgets.values()).index(widget)] - self.tabs.active = tab_id + tab = list(self.widgets.keys())[list(self.widgets.values()).index(widget)] + self.tabs.active = tab.id + + def get_label(self) -> str: + tab = self.tabs.active_tab + return tab.label_text def set_label(self, title: str) -> None: tab = self.tabs.active_tab - tab.update(title) + tab.label = title + + def set_dirty(self, widget: Widget) -> None: + if widget not in self.mounted: + return + + tab = list(self.widgets.keys())[list(self.widgets.values()).index(widget)] + if not tab.label_text.startswith("+ "): + tab.label = "+ " + tab.label_text + + def clear_dirty(self, widget: Widget) -> None: + if widget not in self.mounted: + return + + tab = list(self.widgets.keys())[list(self.widgets.values()).index(widget)] + if tab.label_text.startswith("+ "): + tab.label = tab.label_text[2:] def on_tabs_tab_activated(self, event: Tabs.TabActivated) -> None: - widget = self.widgets[event.tab.id] + widget = self.widgets[event.tab] if self.shown is not None: self.shown.display = False self.shown = widget diff --git a/plugins/notebook_editor/txl_notebook_editor/components.py b/plugins/notebook_editor/txl_notebook_editor/components.py index 513b8e8..c1d5eed 100644 --- a/plugins/notebook_editor/txl_notebook_editor/components.py +++ b/plugins/notebook_editor/txl_notebook_editor/components.py @@ -152,7 +152,15 @@ async def observe_nb_changes(self): kernel_name = kernelspec.get("name") if kernel_name: self.kernel = self.kernels(kernel_name) + elif target == "state": + if "dirty" in events.keys: + dirty = events.keys["dirty"]["newValue"] + if dirty: + self.main_area.set_dirty(self) + else: + self.main_area.clear_dirty(self) elif target == "cells": + self.main_area.set_dirty(self) for event in events: if event.path: continue @@ -229,6 +237,7 @@ async def do_later(): asyncio.create_task(do_later()) elif event.key == Keys.ControlS: await self.contents.save(self.path, self.ynb) + self.main_area.clear_dirty(self) event.stop() elif event.key == Keys.Return or event.key == Keys.Enter: event.stop() diff --git a/plugins/text_editor/txl_text_editor/components.py b/plugins/text_editor/txl_text_editor/components.py index 65d176f..a1fa03c 100644 --- a/plugins/text_editor/txl_text_editor/components.py +++ b/plugins/text_editor/txl_text_editor/components.py @@ -1,3 +1,4 @@ +import asyncio from functools import partial from asphalt.core import Component, Context @@ -5,7 +6,7 @@ from textual.events import Event from textual.keys import Keys -from txl.base import Contents, Editor, Editors, FileOpenEvent +from txl.base import Contents, Editor, Editors, FileOpenEvent, MainArea from txl.text_input import TextInput @@ -17,9 +18,12 @@ class TextEditor(Editor, Container, metaclass=TextEditorMeta): contents: Contents path: str - def __init__(self, contents: Contents) -> None: + def __init__(self, contents: Contents, main_area: MainArea) -> None: super().__init__() self.contents = contents + self.main_area = main_area + self.change_target = asyncio.Queue() + self.change_events = asyncio.Queue() async def on_open(self, event: FileOpenEvent) -> None: await self.open(event.path) @@ -28,11 +32,32 @@ async def open(self, path: str) -> None: self.path = path self.ytext = await self.contents.get(path, type="file") self.editor = TextInput(ytext=self.ytext._ysource, path=path) + self.ytext.observe(self.on_change) + asyncio.create_task(self.observe_changes()) self.mount(self.editor) + def on_change(self, target, events): + self.change_target.put_nowait(target) + self.change_events.put_nowait(events) + + async def observe_changes(self): + while True: + target = await self.change_target.get() + events = await self.change_events.get() + if target == "state": + if "dirty" in events.keys: + dirty = events.keys["dirty"]["newValue"] + if dirty: + self.main_area.set_dirty(self) + else: + self.main_area.clear_dirty(self) + else: + self.main_area.set_dirty(self) + async def on_key(self, event: Event) -> None: if event.key == Keys.ControlS: await self.contents.save(self.path, self.ytext) + self.main_area.clear_dirty(self) event.stop() @@ -46,8 +71,9 @@ async def start( ctx: Context, ) -> None: contents = await ctx.request_resource(Contents) + main_area = await ctx.request_resource(MainArea) - text_editor_factory = partial(TextEditor, contents) + text_editor_factory = partial(TextEditor, contents, main_area) if self.register: editors = await ctx.request_resource(Editors) diff --git a/txl/pyproject.toml b/txl/pyproject.toml index 07880fa..02a1548 100644 --- a/txl/pyproject.toml +++ b/txl/pyproject.toml @@ -23,7 +23,7 @@ classifiers = [ ] dependencies = [ "asphalt >=4.12.0,<5", - "textual[syntax] >=0.51.0,<0.52.0", + "textual[syntax] >=0.52.1,<0.53.0", ] dynamic = ["version"]