add sidebar and start adding functionality
This commit is contained in:
21
src/main.py
21
src/main.py
@@ -5,7 +5,7 @@ from textual.widgets import Header, Footer, TabPane, TabbedContent
|
|||||||
from textual.containers import Vertical
|
from textual.containers import Vertical
|
||||||
from widgets.text_editor import TextEditor, TextArea
|
from widgets.text_editor import TextEditor, TextArea
|
||||||
from widgets.file_tabs import FileTabs
|
from widgets.file_tabs import FileTabs
|
||||||
from widgets.directory_tree_custom import CustomDirectoryTree
|
from widgets.sidebar import Sidebar
|
||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
import argparse
|
import argparse
|
||||||
@@ -28,6 +28,8 @@ class Berry(App):
|
|||||||
"save": "ctrl+s",
|
"save": "ctrl+s",
|
||||||
"save-as": "ctrl+shift+s",
|
"save-as": "ctrl+shift+s",
|
||||||
"settings": "ctrl+f1",
|
"settings": "ctrl+f1",
|
||||||
|
"home": "ctrl+h",
|
||||||
|
"find": "ctrl+f"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +44,9 @@ class Berry(App):
|
|||||||
Binding(self.bind_keys['open'], "open_file", "Open file"),
|
Binding(self.bind_keys['open'], "open_file", "Open file"),
|
||||||
Binding(self.bind_keys['open-folder'], "open_folder", "Open directory"),
|
Binding(self.bind_keys['open-folder'], "open_folder", "Open directory"),
|
||||||
Binding(self.bind_keys['save'], "save", "Save"),
|
Binding(self.bind_keys['save'], "save", "Save"),
|
||||||
Binding(self.bind_keys['save-as'], "save_as", "Save as...")
|
Binding(self.bind_keys['save-as'], "save_as", "Save as..."),
|
||||||
|
Binding(self.bind_keys['home'], "home", "Home"),
|
||||||
|
Binding(self.bind_keys['find'], "find", "Find and replace")
|
||||||
)
|
)
|
||||||
|
|
||||||
for bind in self.BINDINGS:
|
for bind in self.BINDINGS:
|
||||||
@@ -53,16 +57,25 @@ class Berry(App):
|
|||||||
yield Header()
|
yield Header()
|
||||||
with Vertical(id="main"):
|
with Vertical(id="main"):
|
||||||
|
|
||||||
yield CustomDirectoryTree(self.path.parent if self.path.is_file() else self.path)
|
yield Sidebar()
|
||||||
|
|
||||||
self.file_tabs = FileTabs()
|
self.file_tabs = FileTabs()
|
||||||
yield self.file_tabs
|
yield self.file_tabs
|
||||||
|
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
|
async def open_file(self, path: str):
|
||||||
|
self.path = pathlib.Path(path)
|
||||||
|
|
||||||
|
if not self.path.exists():
|
||||||
|
self.notify("That file doesn't exist.", title="File not found", severity="error")
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.file_tabs.open_file(self.path)
|
||||||
|
|
||||||
async def on_ready(self) -> None:
|
async def on_ready(self) -> None:
|
||||||
if self.path.is_file():
|
if self.path.is_file():
|
||||||
await self.file_tabs.open_file(self.path)
|
await self.open_file(self.path)
|
||||||
return
|
return
|
||||||
|
|
||||||
await self.file_tabs.open_home()
|
await self.file_tabs.open_home()
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class CustomDirectoryTree(DirectoryTree):
|
|||||||
|
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
CustomDirectoryTree {
|
CustomDirectoryTree {
|
||||||
dock: left;
|
|
||||||
width: 30;
|
width: 30;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@@ -132,3 +131,5 @@ class CustomDirectoryTree(DirectoryTree):
|
|||||||
event.screen_offset
|
event.screen_offset
|
||||||
), lambda result: self.context_menu_chosen(file_name, result))
|
), lambda result: self.context_menu_chosen(file_name, result))
|
||||||
|
|
||||||
|
async def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected):
|
||||||
|
await self.app.open_file(event.path)
|
||||||
@@ -1,11 +1,50 @@
|
|||||||
from textual.widgets import TabbedContent, TabPane
|
from textual.widgets import TabbedContent, TabPane
|
||||||
|
from textual.reactive import reactive
|
||||||
|
|
||||||
from widgets.text_editor import TextEditor
|
from widgets.text_editor import TextEditor
|
||||||
from widgets.home_page import HomePage
|
from widgets.home_page import HomePage
|
||||||
|
|
||||||
import os
|
import os, pathlib
|
||||||
|
|
||||||
|
|
||||||
|
FILE_EXTENSION_LANG = {
|
||||||
|
".py": "python"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileTab(TabPane):
|
||||||
|
saved = reactive(True)
|
||||||
|
changed = reactive(False)
|
||||||
|
|
||||||
|
def __init__(self, path: str):
|
||||||
|
file_name = os.path.basename(path)
|
||||||
|
|
||||||
|
super().__init__(f"[ansi_bright_green][/] {file_name}")
|
||||||
|
self.file_name = file_name
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def watch_saved(self, saved_value: bool):
|
||||||
|
if self.saved == saved_value:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.changed = True
|
||||||
|
icon = "[ansi_bright_green][/]" if self.saved else "[ansi_yellow][/]"
|
||||||
|
|
||||||
|
self._title = self.render_str(f"{icon} {self.file_name}")
|
||||||
|
parent: TabbedContent = self.parent
|
||||||
|
|
||||||
|
parent.recompose()
|
||||||
|
|
||||||
|
def on_text_area_changed(self, event: TextEditor.Changed):
|
||||||
|
self.saved = False
|
||||||
|
|
||||||
|
class HomeTab(TabPane):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("Home")
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
yield HomePage()
|
||||||
|
|
||||||
class FileTabs(TabbedContent):
|
class FileTabs(TabbedContent):
|
||||||
DEFAULT_CSS = """
|
DEFAULT_CSS = """
|
||||||
FileTabs {
|
FileTabs {
|
||||||
@@ -13,22 +52,60 @@ class FileTabs(TabbedContent):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def open_home(self) -> None:
|
async def save(self) -> None:
|
||||||
new_pane = TabPane("Home")
|
if self.tab_count == 0:
|
||||||
new_pane.path = None
|
return
|
||||||
|
|
||||||
await self.add_pane(new_pane)
|
if not isinstance(self.active_pane, FileTab):
|
||||||
await new_pane.mount(HomePage())
|
return
|
||||||
|
|
||||||
|
file_tab: FileTab = self.active_pane
|
||||||
|
|
||||||
|
text_editor: TextEditor = file_tab.query_one(TextEditor)
|
||||||
|
if text_editor.read_only:
|
||||||
|
return
|
||||||
|
|
||||||
|
with open(file_tab.path, "w") as f:
|
||||||
|
f.write(text_editor.text)
|
||||||
|
|
||||||
|
file_tab.saved = True
|
||||||
|
|
||||||
|
async def open_home(self) -> None:
|
||||||
|
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:
|
if self.tab_count > 0:
|
||||||
for tab in self.query(TabPane):
|
if isinstance(self.active_pane, HomeTab):
|
||||||
if tab.path == path:
|
self.remove_pane(self.active)
|
||||||
self.active = tab.id
|
else:
|
||||||
return
|
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 = TabPane(os.path.basename(path))
|
new_pane = FileTab(path)
|
||||||
new_pane.path = path
|
|
||||||
|
path = pathlib.Path(path)
|
||||||
|
|
||||||
|
content: str = ""
|
||||||
|
is_binary: bool = False
|
||||||
|
try:
|
||||||
|
with open(path, "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
is_binary = True
|
||||||
|
|
||||||
await self.add_pane(new_pane)
|
await self.add_pane(new_pane)
|
||||||
await new_pane.mount(TextEditor())
|
|
||||||
|
text_editor: TextEditor = None
|
||||||
|
if not is_binary:
|
||||||
|
text_editor = TextEditor(content, language=FILE_EXTENSION_LANG.get(path.suffix, None))
|
||||||
|
else:
|
||||||
|
text_editor = TextEditor(is_binary=is_binary)
|
||||||
|
await new_pane.mount(text_editor)
|
||||||
|
|
||||||
|
self.active = new_pane.id
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ class HomePage(Vertical):
|
|||||||
}
|
}
|
||||||
|
|
||||||
#banner {
|
#banner {
|
||||||
height: 6; # probs will need to be updated
|
height: 7; # probs will need to be updated
|
||||||
margin-bottom: 1;
|
margin-bottom: 1;
|
||||||
|
|
||||||
Rule {
|
Rule {
|
||||||
|
|||||||
53
src/widgets/sidebar.py
Normal file
53
src/widgets/sidebar.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
from textual.containers import Vertical, Container
|
||||||
|
from textual.widgets import Button, ContentSwitcher, Static
|
||||||
|
|
||||||
|
from widgets.directory_tree_custom import CustomDirectoryTree
|
||||||
|
|
||||||
|
|
||||||
|
class Sidebar(Container):
|
||||||
|
DEFAULT_CSS = """
|
||||||
|
Sidebar {
|
||||||
|
dock: left;
|
||||||
|
height: 1fr;
|
||||||
|
width: auto;
|
||||||
|
layout: horizontal;
|
||||||
|
|
||||||
|
#sidebar-buttons {
|
||||||
|
padding: 1;
|
||||||
|
border: $surface tall;
|
||||||
|
width: 11;
|
||||||
|
|
||||||
|
Button {
|
||||||
|
max-width: 7;
|
||||||
|
margin-bottom: 1;
|
||||||
|
color: $primary;
|
||||||
|
text-style: bold;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
text-style: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSwitcher {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed):
|
||||||
|
content_switcher: ContentSwitcher = self.query_one(ContentSwitcher)
|
||||||
|
|
||||||
|
if content_switcher.current == event.button.id:
|
||||||
|
content_switcher.current = None
|
||||||
|
return
|
||||||
|
|
||||||
|
content_switcher.current = event.button.id
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
with Vertical(id="sidebar-buttons"):
|
||||||
|
yield Button("", id="files")
|
||||||
|
yield Button("", id="plugins")
|
||||||
|
with ContentSwitcher():
|
||||||
|
yield CustomDirectoryTree(".", id="files")
|
||||||
|
yield Static("sorry, plugins aren't not done yet", id="plugins")
|
||||||
@@ -2,5 +2,9 @@ from textual.widgets import TextArea
|
|||||||
|
|
||||||
|
|
||||||
class TextEditor(TextArea):
|
class TextEditor(TextArea):
|
||||||
def __init__(self, text = "", language = None, read_only = False):
|
def __init__(self, text = "", language = None, read_only = False, is_binary: bool = False):
|
||||||
super().__init__(text, language=language, placeholder="Type here . . .", read_only=read_only, theme="vscode_dark", show_line_numbers=True, tab_behavior="indent")
|
if is_binary:
|
||||||
|
super().__init__(placeholder="This file is binary and not be read.", read_only=True, theme="css", tab_behavior="indent")
|
||||||
|
return
|
||||||
|
|
||||||
|
super().__init__(text, language=language, placeholder="Type here...", read_only=read_only, soft_wrap=False, theme="css", show_line_numbers=True, tab_behavior="indent")
|
||||||
13
test.py
13
test.py
@@ -1,13 +0,0 @@
|
|||||||
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
|
|
||||||
Reference in New Issue
Block a user