From 2e80aa755320de6f38f108d3a79006c7372fb690 Mon Sep 17 00:00:00 2001 From: SpookyDervish Date: Tue, 28 Jul 2026 15:46:23 +1000 Subject: [PATCH] saving and creating new files --- src/main.py | 6 ++ src/widgets/file_tabs.py | 118 ++++++++++++++++++++++++++++++++------- src/widgets/home_page.py | 5 -- 3 files changed, 103 insertions(+), 26 deletions(-) diff --git a/src/main.py b/src/main.py index a64ed16..6b7748e 100644 --- a/src/main.py +++ b/src/main.py @@ -64,6 +64,12 @@ class Berry(App): yield Footer() + async def action_save(self): + await self.file_tabs.save() + + async def action_new(self): + await self.file_tabs.new_file() + async def open_file(self, path: str): self.path = pathlib.Path(path) diff --git a/src/widgets/file_tabs.py b/src/widgets/file_tabs.py index 92f81d2..799d17c 100644 --- a/src/widgets/file_tabs.py +++ b/src/widgets/file_tabs.py @@ -1,6 +1,10 @@ -from textual.widgets import TabbedContent, TabPane +from textual.widgets import TabbedContent, Tab, TabPane +from textual.widget import Widget +from textual.widgets._tabbed_content import ContentTabs from textual.reactive import reactive +from textual_fspicker import FileOpen, FileSave, SelectDirectory + from widgets.text_editor import TextEditor from widgets.home_page import HomePage @@ -8,7 +12,9 @@ import os, pathlib FILE_EXTENSION_LANG = { - ".py": "python" + ".py": "python", + ".json": "json", + ".md": "markdown" } @@ -17,23 +23,44 @@ class FileTab(TabPane): changed = reactive(False) def __init__(self, path: str): - file_name = os.path.basename(path) + if path != None: + file_name = os.path.basename(path) + else: + file_name = "New File" super().__init__(f"[ansi_bright_green][/] {file_name}") self.file_name = file_name self.path = path + self.last_saved_value = True def watch_saved(self, saved_value: bool): - if self.saved == saved_value: - return + if self.last_saved_value != saved_value: + self.changed = True + self.last_saved_value = saved_value + + self.refresh_name() - self.changed = True + def refresh_name(self): icon = "[ansi_bright_green][/]" if self.saved else "[ansi_yellow][/]" + self.parent.parent.get_tab(self.id).label = f"{icon} {self.file_name}" - self._title = self.render_str(f"{icon} {self.file_name}") - parent: TabbedContent = self.parent + def update_language(self): + try: + text_editor: TextEditor = self.query_one(TextEditor) + except: + return - parent.recompose() + extension = pathlib.Path(self.path).suffix + + text_editor.language = FILE_EXTENSION_LANG.get(extension) + + def update_path(self, new_path: str): + self.path = new_path + self.file_name = os.path.basename(new_path) + + self.saved = True + self.refresh_name() + self.update_language() def on_text_area_changed(self, event: TextEditor.Changed): self.saved = False @@ -52,7 +79,35 @@ class FileTabs(TabbedContent): } """ - async def save(self) -> None: + async def clean_up_tabs(self, new_path: str): + if self.tab_count > 0: + if isinstance(self.active_pane, HomeTab): + self.remove_pane(self.active) + else: + if not self.active_pane.changed: + await self.remove_pane(self.active) + + for tab in self.query(TabPane): + if isinstance(self.active_pane, HomeTab): + continue + + if tab.path == new_path: + self.active = tab.id + return False + return True + + async def new_file(self): + new_tab = FileTab(None) + + await self.add_pane(new_tab) + + text_editor = TextEditor() + await new_tab.mount(text_editor) + + self.active = new_tab.id + text_editor.focus() + + async def save_as(self): if self.tab_count == 0: return @@ -65,6 +120,35 @@ class FileTabs(TabbedContent): if text_editor.read_only: return + def callback(path: str | None): + if path == None: + return + + with open(path, "w") as f: + f.write(text_editor.text) + + file_tab.update_path(path) + + + await self.app.push_screen(FileSave(), callback=callback) + + async def save(self): + if self.tab_count == 0: + return + + if not isinstance(self.active_pane, FileTab): + return + + file_tab: FileTab = self.active_pane + + text_editor: TextEditor = file_tab.query_one(TextEditor) + if text_editor.read_only: + return + + if file_tab.path == None: # new file + await self.save_as() + return + with open(file_tab.path, "w") as f: f.write(text_editor.text) @@ -74,17 +158,9 @@ class FileTabs(TabbedContent): await self.add_pane(HomeTab()) async def open_file(self, path: str) -> None: - if self.tab_count > 0: - if isinstance(self.active_pane, HomeTab): - self.remove_pane(self.active) - else: - if not self.active_pane.changed: - self.remove_pane(self.active) - - for tab in self.query(TabPane): - if tab.path == path: - self.active = tab.id - return + opened_new_tab = await self.clean_up_tabs(path) + if not opened_new_tab: + return new_pane = FileTab(path) diff --git a/src/widgets/home_page.py b/src/widgets/home_page.py index aef4e37..b4268d5 100644 --- a/src/widgets/home_page.py +++ b/src/widgets/home_page.py @@ -62,12 +62,7 @@ class HomePage(Vertical): } } """ - - @on(EffectLabel.EffectFinished) - def restart_effect(self): - self.banner_label.run_worker(self.banner_label.run_effect, exclusive=True) - def compose(self): with Horizontal(id="banner"): yield Rule()