diff --git a/src/main.py b/src/main.py index 33c21d8..bd4d0e5 100644 --- a/src/main.py +++ b/src/main.py @@ -1,15 +1,77 @@ from textual.app import App, ComposeResult +from textual.binding import Binding -from textual.widgets import Footer -from widgets.text_editor import TextEditor +from textual.widgets import Header, Footer, TabPane, TabbedContent +from textual.containers import Vertical +from widgets.text_editor import TextEditor, TextArea +from widgets.file_tabs import FileTabs +from widgets.directory_tree_custom import CustomDirectoryTree + +import sys, os +import argparse +import pathlib class Berry(App): + DEFAULT_CSS = """ + Berry { + #main { + padding: 1; + } + } + """ + + bind_keys = { + "new": "ctrl+n", + "open": "ctrl+o", + "open-folder": "ctrl+shift+o", + "save": "ctrl+s", + "save-as": "ctrl+shift+s", + "settings": "ctrl+f1", + } + + + + def __init__(self, path: str): + super().__init__() + self.open_tab = None + self.path = pathlib.Path(path) + + self.BINDINGS = ( + Binding(self.bind_keys['new'], "new", "New file"), + Binding(self.bind_keys['open'], "open_file", "Open file"), + Binding(self.bind_keys['open-folder'], "open_folder", "Open directory"), + Binding(self.bind_keys['save'], "save", "Save"), + Binding(self.bind_keys['save-as'], "save_as", "Save as...") + ) + + for bind in self.BINDINGS: + self._bindings._add_binding(bind) + self.refresh_bindings() + def compose(self) -> ComposeResult: - yield TextEditor(language="python") + yield Header() + with Vertical(id="main"): + + yield CustomDirectoryTree(self.path.parent if self.path.is_file() else self.path) + + self.file_tabs = FileTabs() + yield self.file_tabs + yield Footer() + async def on_ready(self) -> None: + if self.path.is_file(): + await self.file_tabs.open_file(self.path) + return + + await self.file_tabs.open_home() + if __name__ == "__main__": - app = Berry() + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("path", default=".") + args = arg_parser.parse_args() + + app = Berry(args.path) app.run() \ No newline at end of file diff --git a/src/widgets/directory_tree_custom.py b/src/widgets/directory_tree_custom.py index 02d5a76..bb0f410 100644 --- a/src/widgets/directory_tree_custom.py +++ b/src/widgets/directory_tree_custom.py @@ -2,24 +2,29 @@ from textual.widgets import DirectoryTree, Rule from textual.widgets.tree import TreeNode from textual.events import MouseDown -from prompt import Prompt -from context_menu import ContextMenu, NoSelectStatic +from widgets.prompt import Prompt +from widgets.context_menu import ContextMenu, NoSelectStatic import os, shutil class CustomDirectoryTree(DirectoryTree): + DEFAULT_CSS = """ + CustomDirectoryTree { + dock: left; + width: 30; + } + """ + ICON_NODE = " " ICON_NODE_EXPANDED = " " ICON_FILE = " " - + def __init__(self, path, *, name = None, id = None, classes = None, disabled = False): super().__init__(path, name=name, id=id, classes=classes, disabled=disabled) self.right_clicked_node: TreeNode | None = None - - def context_menu_chosen(self, file_name: str, result: str): if result == "Delete": def delete_confirm(will_delete: bool | None): diff --git a/src/widgets/file_tabs.py b/src/widgets/file_tabs.py new file mode 100644 index 0000000..ffdbd1e --- /dev/null +++ b/src/widgets/file_tabs.py @@ -0,0 +1,34 @@ +from textual.widgets import TabbedContent, TabPane + +from widgets.text_editor import TextEditor +from widgets.home_page import HomePage + +import os + + +class FileTabs(TabbedContent): + DEFAULT_CSS = """ + FileTabs { + margin: 0 1; + } + """ + + async def open_home(self) -> None: + new_pane = TabPane("Home") + new_pane.path = None + + await self.add_pane(new_pane) + await new_pane.mount(HomePage()) + + async def open_file(self, path: str) -> None: + if self.tab_count > 0: + for tab in self.query(TabPane): + if tab.path == path: + self.active = tab.id + return + + new_pane = TabPane(os.path.basename(path)) + new_pane.path = path + + await self.add_pane(new_pane) + await new_pane.mount(TextEditor()) \ No newline at end of file diff --git a/src/widgets/home_page.py b/src/widgets/home_page.py new file mode 100644 index 0000000..45fe7b4 --- /dev/null +++ b/src/widgets/home_page.py @@ -0,0 +1,102 @@ +from textual.widgets import Static, Rule +from textual.containers import Vertical, Horizontal, Center +from textual import on + +from textualeffects.widgets import EffectLabel +from terminaltexteffects.utils.graphics import Color + +import pyfiglet + + +class HomePage(Vertical): + DEFAULT_CSS = """ + HomePage { + content-align: center top; + + Static { + text-align: center; + } + + #banner { + height: 6; # probs will need to be updated + margin-bottom: 1; + + Rule { + color: $surface-lighten-2; + height: 100%; + content-align: center middle; + margin: 0 3; + } + + EffectLabel { + height: 100%; + } + + + } + + #bindings { + Horizontal { + max-width: 50; + + width: 50%; + margin-top: 1; + height: 1; + + .bind-name { + width: 75%; + } + .bind-keys { + width: 25%; + } + + Static { + text-align: left; + link-background: transparent; + link-color: $primary; + } + + + } + + } + } + """ + + @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() + self.banner_label = EffectLabel(pyfiglet.figlet_format("berry", font="sub-zero"), effect="Highlight", config={ + "final_gradient_stops": [Color("#f263ff"), Color("#946ff2")], + "cycles": 0 + }) + yield self.banner_label + yield Rule() + + yield Static("[dim]The text editor that no one asked for and didn't need.[/]") + + # bindings + with Center(id="bindings"): + binds = self.app.bind_keys + + with Horizontal(): + yield Static("[b] New file[/]", classes="bind-name") + yield Static(f"[@click=app.new]{binds["new"]}[/]", classes="bind-keys") + + with Horizontal(): + yield Static("[b] Open file[/]", classes="bind-name") + yield Static(f"[@click=app.open]{binds["open"]}[/]", classes="bind-keys") + + with Horizontal(): + yield Static("[b] Open Folder[/]", classes="bind-name") + yield Static(f"[@click=app.open_folder]{binds["open-folder"]}[/]", classes="bind-keys") + + with Horizontal(): + yield Static("[b] Settings[/]", classes="bind-name") + yield Static(f"[@click=app.settings]{binds["settings"]}[/]", classes="bind-keys") + \ No newline at end of file diff --git a/src/widgets/text_editor.py b/src/widgets/text_editor.py index 543a61c..433e690 100644 --- a/src/widgets/text_editor.py +++ b/src/widgets/text_editor.py @@ -3,4 +3,4 @@ from textual.widgets import TextArea class TextEditor(TextArea): def __init__(self, text = "", language = None, read_only = False): - super().__init__(text, language=language, placeholder="Type here . . .", read_only=read_only, theme="css") \ No newline at end of file + super().__init__(text, language=language, placeholder="Type here . . .", read_only=read_only, theme="vscode_dark", show_line_numbers=True, tab_behavior="indent") \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..435bbfa --- /dev/null +++ b/test.py @@ -0,0 +1,13 @@ +import pyfiglet + +text_to_render = "Test" +fonts = pyfiglet.FigletFont.getFonts() + +for font in fonts: + try: + print(f"\nFont: {font}") + result = pyfiglet.figlet_format(text_to_render, font=font) + print(result) + except Exception: + # Prevents the script from crashing if a specific font configuration fails + continue \ No newline at end of file