saving and creating new files

This commit is contained in:
2026-07-28 15:46:23 +10:00
parent 44aa152295
commit 2e80aa7553
3 changed files with 103 additions and 26 deletions

View File

@@ -64,6 +64,12 @@ class Berry(App):
yield Footer() 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): async def open_file(self, path: str):
self.path = pathlib.Path(path) self.path = pathlib.Path(path)

View File

@@ -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.reactive import reactive
from textual_fspicker import FileOpen, FileSave, SelectDirectory
from widgets.text_editor import TextEditor from widgets.text_editor import TextEditor
from widgets.home_page import HomePage from widgets.home_page import HomePage
@@ -8,7 +12,9 @@ import os, pathlib
FILE_EXTENSION_LANG = { FILE_EXTENSION_LANG = {
".py": "python" ".py": "python",
".json": "json",
".md": "markdown"
} }
@@ -17,23 +23,44 @@ class FileTab(TabPane):
changed = reactive(False) changed = reactive(False)
def __init__(self, path: str): 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}") super().__init__(f"[ansi_bright_green][/] {file_name}")
self.file_name = file_name self.file_name = file_name
self.path = path self.path = path
self.last_saved_value = True
def watch_saved(self, saved_value: bool): def watch_saved(self, saved_value: bool):
if self.saved == saved_value: if self.last_saved_value != saved_value:
return 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][/]" 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}") def update_language(self):
parent: TabbedContent = self.parent 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): def on_text_area_changed(self, event: TextEditor.Changed):
self.saved = False 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: if self.tab_count == 0:
return return
@@ -65,6 +120,35 @@ class FileTabs(TabbedContent):
if text_editor.read_only: if text_editor.read_only:
return 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: with open(file_tab.path, "w") as f:
f.write(text_editor.text) f.write(text_editor.text)
@@ -74,17 +158,9 @@ class FileTabs(TabbedContent):
await self.add_pane(HomeTab()) await self.add_pane(HomeTab())
async def open_file(self, path: str) -> None: async def open_file(self, path: str) -> None:
if self.tab_count > 0: opened_new_tab = await self.clean_up_tabs(path)
if isinstance(self.active_pane, HomeTab): if not opened_new_tab:
self.remove_pane(self.active) return
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
new_pane = FileTab(path) new_pane = FileTab(path)

View File

@@ -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): def compose(self):
with Horizontal(id="banner"): with Horizontal(id="banner"):
yield Rule() yield Rule()