continued adding support for other text editors, added a home screen

This commit is contained in:
2026-05-16 21:18:21 +10:00
parent 7df6ce10c7
commit fc07ffbfa8
6 changed files with 255 additions and 116 deletions

103
home_page.py Normal file
View File

@@ -0,0 +1,103 @@
from textual.containers import Center, Vertical, Horizontal
from textual.widgets import Rule, Static
from textual import on
from textualeffects.widgets import EffectLabel
from textualeffects.effects import effects
from terminaltexteffects.utils.graphics import Color
class HomePage(Vertical):
LOGO = r""" ______ ______ ______ ______ __ __
/\ == \ /\ ___\ /\ == \ /\ == \ /\ \_\ \
\ \ __< \ \ __\ \ \ __< \ \ __< \ \____ \
\ \_____\ \ \_____\ \ \_\ \_\ \ \_\ \_\ \/\_____\
\/_____/ \/_____/ \/_/ /_/ \/_/ /_/ \/_____/
"""
DEFAULT_CSS = """
HomePage {
content-align: center top;
Static {
text-align: center;
}
#banner {
height: 5; # 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: 85%;
}
.bind-keys {
width: 15%;
}
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(self.LOGO, 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.bindings
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] Settings[/]", classes="bind-name")
yield Static(f"[@click=app.settings]{binds["settings"]}[/]", classes="bind-keys")

64
main.py
View File

@@ -8,6 +8,8 @@ from textual_fspicker import FileOpen, FileSave
from pathlib import Path from pathlib import Path
from home_page import HomePage
from assets.theme_mappings import theme_mappings from assets.theme_mappings import theme_mappings
from plugin_loader import PluginLoader from plugin_loader import PluginLoader
from settings import SettingsScreen from settings import SettingsScreen
@@ -32,19 +34,32 @@ class Berry(App):
CSS_PATH = "assets/style.tcss" CSS_PATH = "assets/style.tcss"
SUB_TITLE = "New File" SUB_TITLE = "New File"
BINDINGS = [
Binding("ctrl+o", "open", "Open File"),
Binding("ctrl+n", "new", "New File"),
Binding("ctrl+s", "save", "Save"),
Binding("ctrl+shift+s", "save_as", "Save As...", priority=True),
Binding("ctrl+f", "find", "Find", priority=True),
Binding("ctrl+f1", "settings", "Settings")
]
def __init__(self, path: str): def __init__(self, path: str):
self.config_handler = ConfigHandler(self)
self.bindings = eval(self.config_handler.get("editor", "bindings"))
super().__init__() super().__init__()
self.path = path self.path = path
self.config_handler = ConfigHandler(self)
self.BINDINGS = (
Binding(self.bindings["new"], "new", "New File"),
Binding(self.bindings["open"], "open", "Open File"),
Binding(self.bindings["save"], "save", "Save File"),
Binding(self.bindings["save-as"], "save_as", "Save File As..."),
Binding(self.bindings["find"], "find", "Find and Replace"),
Binding(self.bindings["settings"], "settings", "Settings"),
)
for binding in self.BINDINGS:
self._bindings._add_binding(binding)
self.refresh_bindings()
self.current_editor = None
self.file_tabs = {}
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield Header() yield Header()
@@ -59,14 +74,18 @@ class Berry(App):
yield CustomDirectoryTree(self.path, id="directory") yield CustomDirectoryTree(self.path, id="directory")
with Vertical(id="editor"): with Vertical(id="editor"):
first_tab = Tab("New File") first_tab = Tab("Home")
first_tab.file_path = None first_tab.file_path = "berry://home"
self.file_tabs["berry://home"] = first_tab
yield Tabs( yield Tabs(
first_tab, first_tab,
id="file-tabs" id="file-tabs"
) )
yield TextArea.code_editor(placeholder="This file is empty.", theme="css", id="code-editor", disabled=True, soft_wrap=True, show_line_numbers=bool(int(self.config_handler.get("editor", "line_numbers"))))
with Vertical() as editor:
yield HomePage()
self.current_editor = editor
#if os.name == "nt": #if os.name == "nt":
#with Vertical(id="console-container"): #with Vertical(id="console-container"):
@@ -201,7 +220,9 @@ class Berry(App):
tabs.active = self.file_tabs[str(event.path)].id tabs.active = self.file_tabs[str(event.path)].id
def open_text_editor(self, file_content: bytes, file_extension: str): def open_text_editor(self, file_content: bytes, file_extension: str):
code_editor: TextArea = self.query_one("#code-editor") code_editor = TextArea.code_editor(placeholder="This file is empty.", theme="css", disabled=True, soft_wrap=bool(int(self.config_handler.get("editor", "word_wrap"))), show_line_numbers=bool(int(self.config_handler.get("editor", "line_numbers"))))
self.current_editor.mount(code_editor)
code_editor.language = theme_mappings.get(file_extension, None) code_editor.language = theme_mappings.get(file_extension, None)
@@ -215,10 +236,12 @@ class Berry(App):
code_editor.focus() code_editor.focus()
def open_editor(self, file_extension: str, file_content: str): def open_editor(self, file_extension: str, file_content: bytes):
default_editors: dict = eval(self.config_handler.get("editor", "default_editors")) default_editors: dict = eval(self.config_handler.get("editor", "default_editors"))
editor_for_file = default_editors.get(file_extension, None) editor_for_file = default_editors.get(file_extension, None)
self.current_editor.remove_children()
if not editor_for_file: if not editor_for_file:
self.open_text_editor(file_content, file_extension) self.open_text_editor(file_content, file_extension)
@@ -230,8 +253,12 @@ class Berry(App):
else: else:
self.open_file = getattr(event.tab, "file_path") self.open_file = getattr(event.tab, "file_path")
if self.open_file and self.open_file.startswith(r"berry://"):
return
self.switching = True self.switching = True
content = b"" content = b""
file_extension = "" file_extension = ""
@@ -265,8 +292,6 @@ class Berry(App):
event.window.remove_window() event.window.remove_window()
def on_text_area_changed(self, event: TextArea.Changed): def on_text_area_changed(self, event: TextArea.Changed):
if event.text_area.id != "code-editor":
return
if self.switching: if self.switching:
self.switching = False self.switching = False
return return
@@ -301,10 +326,9 @@ class Berry(App):
self.open_file = None self.open_file = None
self.switching = True self.switching = True
code_editor: TextArea = self.query_one("#code-editor")
code_editor.disabled = False # open empty editor
code_editor.text = "" self.open_editor("", b"")
def done_saving(self, result): def done_saving(self, result):
if result is None: return if result is None: return
@@ -342,8 +366,6 @@ class Berry(App):
return super().action_quit() return super().action_quit()
def on_ready(self): def on_ready(self):
# src/main.py: Tab<>
self.file_tabs = {}
self.open_file = None self.open_file = None
self.unsaved_files = {} # list of paths self.unsaved_files = {} # list of paths
self.switching = False self.switching = False

View File

@@ -2,5 +2,6 @@
"name": "Markdown Reader", "name": "Markdown Reader",
"author": "SpookyDervish", "author": "SpookyDervish",
"version": "1.0.0", "version": "1.0.0",
"description": "",
"dependencies": [] "dependencies": []
} }

View File

@@ -1,5 +1,5 @@
from textual.screen import ModalScreen from textual.screen import ModalScreen
from textual.widgets import Label, Select, TabbedContent, TabPane, Switch, Input, Rule, Static from textual.widgets import Label, Select, TextArea, TabbedContent, TabPane, Switch, Input, Rule, Static
from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll
from textual.binding import Binding from textual.binding import Binding
@@ -70,11 +70,15 @@ class SettingsScreen(ModalScreen):
self.dismiss() self.dismiss()
def on_switch_changed(self, event: Switch.Changed): def on_switch_changed(self, event: Switch.Changed):
text_area = self.app.current_editor.query_one_optional(TextArea)
if event.switch.id == "word-wrap": if event.switch.id == "word-wrap":
self.app.query_one("#code-editor").soft_wrap = event.value if text_area:
text_area.soft_wrap = event.value
self.app.config_handler.set("editor", "word_wrap", str(int(event.value))) self.app.config_handler.set("editor", "word_wrap", str(int(event.value)))
elif event.switch.id == "line-numbers": elif event.switch.id == "line-numbers":
self.app.query_one("#code-editor").show_line_numbers = event.value if text_area:
text_area.show_line_numbers = event.value
self.app.config_handler.set("editor", "line_numbers", str(int(event.value))) self.app.config_handler.set("editor", "line_numbers", str(int(event.value)))
elif event.switch.id == "plugins-enabled": elif event.switch.id == "plugins-enabled":
self.app.config_handler.set("plugins", "enabled", str(int(event.value))) self.app.config_handler.set("plugins", "enabled", str(int(event.value)))

View File

@@ -76,7 +76,9 @@ class ConfigHandler:
self.write_settings() self.write_settings()
def apply_settings(self): def apply_settings(self):
self.app.query_one("#code-editor").soft_wrap = bool(int(self.get("editor", "word_wrap"))) if self.app.current_editor and self.app.current_editor.id == "code-editor":
self.app.current_editor.soft_wrap = bool(int(self.get("editor", "word_wrap")))
self.app.theme = self.get("appearance", "colour_theme") self.app.theme = self.get("appearance", "colour_theme")
def write_settings(self): def write_settings(self):
@@ -94,7 +96,15 @@ class ConfigHandler:
self.config["editor"] = { self.config["editor"] = {
"word_wrap": "0", "word_wrap": "0",
"line_numbers": "1", "line_numbers": "1",
"default_editors": {} "default_editors": {},
"bindings": {
"open": "ctrl+o",
"new": "ctrl+n",
"save": "ctrl+s",
"save-as": "ctrl+shift+s",
"find": "ctrl+f",
"settings": "ctrl+f1"
}
} }
self.config["plugins"] = { self.config["plugins"] = {
"enabled": "1", "enabled": "1",

View File

@@ -1 +0,0 @@
print("Hello, World!")