continued adding support for other text editors, added a home screen
This commit is contained in:
103
home_page.py
Normal file
103
home_page.py
Normal 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
64
main.py
@@ -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
|
||||||
|
|||||||
@@ -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": []
|
||||||
}
|
}
|
||||||
10
settings.py
10
settings.py
@@ -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)))
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user