working on multiple editor types

This commit is contained in:
2026-05-16 17:50:51 +10:00
parent 44c3ebe887
commit 7df6ce10c7
10 changed files with 411 additions and 375 deletions

View File

@@ -14,4 +14,5 @@ theme_mappings = {
"yaml": "yaml",
"md": "markdown",
"gitignore": "markdown",
"lua": "lua"
}

View File

@@ -9,13 +9,18 @@ import os, shutil
class CustomDirectoryTree(DirectoryTree):
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, result):
def context_menu_chosen(self, file_name: str, result: str):
if result == "Delete":
def delete_confirm(will_delete: bool | None):
if will_delete == True:
@@ -44,7 +49,7 @@ class CustomDirectoryTree(DirectoryTree):
self.right_clicked_node = None
self.app.push_screen(Prompt(f"Enter the new name for \"{self.right_clicked_node.label}\".", "string", "Rename"), rename_confirm)
elif result == "New Folder":
elif result == "New folder":
def new_folder(folder_name: str | None):
if folder_name == None: return
if folder_name.strip() == "":
@@ -60,7 +65,7 @@ class CustomDirectoryTree(DirectoryTree):
return
self.app.push_screen(Prompt("Enter the name of the new folder.", "string", "Create New Folder"), new_folder)
elif result == "New File":
elif result == "New file":
def new_file(file_name: str | None):
if file_name == None: return
if file_name.strip() == "":
@@ -82,6 +87,24 @@ class CustomDirectoryTree(DirectoryTree):
return
self.app.push_screen(Prompt("Enter the name of the new file", "string", "Create New File"), new_file)
elif result == "Open with...":
file_extension = file_name.rsplit(".")[1]
default_editors = eval(self.app.config_handler.get("editor", "default_editors"))
default_editor = default_editors.get(file_extension, "Text Editor")
def open_with(chosen_editor):
self.notify(str(chosen_editor))
self.app.push_screen(Prompt(
"Editor for file:",
"list",
"Choose editor for file",
values=[
default_editor + " (Default)",
"Text Editor"
],
allow_blank=False
), open_with)
def on_mouse_down(self, event: MouseDown):
if event.button != 3 or not "line" in event.style.meta:
@@ -89,16 +112,18 @@ class CustomDirectoryTree(DirectoryTree):
selected_node = self.get_node_at_line(event.style.meta["line"])
self.right_clicked_node = selected_node
spacer = NoSelectStatic(f'[d]{"-" * 17}[/]')
options = None
if self._safe_is_dir(self.right_clicked_node.data.path):
options = ["New Folder", "New File", NoSelectStatic(f'[d]{"-" * 17}[/]'), "Delete", "Rename"]
options = ["New folder", "New file", spacer, "Delete", "Rename"]
else:
options = ["Delete", "Rename"]
options = ["Delete", "Rename", spacer, "Open with..."]
file_name = str(self.right_clicked_node.label) if len(self.right_clicked_node.label) <= 17 else self.right_clicked_node.label[:14] + "..."
self.app.push_screen(ContextMenu(
[NoSelectStatic(f"[b]{file_name}[/]"), NoSelectStatic(f'[d]{"-" * 17}[/]')] + options,
event.screen_offset
), self.context_menu_chosen)
), lambda result: self.context_menu_chosen(file_name, result))

625
main.py
View File

@@ -21,341 +21,344 @@ import os, sys
class Watcher(FileSystemEventHandler):
def __init__(self, app: App):
super().__init__()
self.app = app
def __init__(self, app: App):
super().__init__()
self.app = app
async def on_any_event(self, event):
await self.app.query_one(DirectoryTree).reload()
async def on_any_event(self, event):
await self.app.query_one(DirectoryTree).reload()
class Berry(App):
CSS_PATH = "assets/style.tcss"
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):
super().__init__()
self.path = path
self.config_handler = ConfigHandler(self)
def compose(self) -> ComposeResult:
yield Header()
with Vertical(id="sidebar"):
with HorizontalGroup(id="sidebar-buttons"):
yield Button("📂")
yield Button("🔍")
with ContentSwitcher(initial="files", id="sidebar-switcher"):
with Vertical(id="files"):
yield Static("EXPLORER")
yield CustomDirectoryTree(self.path, id="directory")
with Vertical(id="editor"):
first_tab = Tab("New File")
first_tab.file_path = None
yield Tabs(
first_tab,
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"))))
#if os.name == "nt":
#with Vertical(id="console-container"):
# yield RichLog(id="console")
# yield Input(placeholder="> ", id="console-input")
#else:
# yield Terminal(command="bash", id="terminal")
yield Footer()
if bool(int(self.config_handler.get("plugins", "enabled"))) == True:
yield PluginLoader()
def action_settings(self):
self.push_screen(SettingsScreen())
def get_system_commands(self, screen):
yield SystemCommand(
"Quit the application",
"Quit the application as soon as possible",
self.action_quit,
)
if screen.query("HelpPanel"):
yield SystemCommand(
"Hide keys and help panel",
"Hide the keys and widget help panel",
self.action_hide_help_panel,
)
else:
yield SystemCommand(
"Show keys and help panel",
"Show help for the focused widget and a summary of available keys",
self.action_show_help_panel,
)
yield SystemCommand("Settings", "Open the settings menu", self.action_settings)
async def chose_file_to_open(self, result):
if result == None: return
result = str(result)
if self.open_file == result:
return
def is_within_directory(file_path: str, directory: str) -> bool:
file_path = Path(file_path).resolve()
directory = Path(directory).resolve()
return directory in file_path.parents
self.switching = True
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file not in self.unsaved_files:
if self.open_file:
self.file_tabs.pop(self.open_file)
tabs.remove_tab(tabs.active_tab)
self.open_file = result
inside_dir = is_within_directory(result, self.path)
self.sub_title = os.path.basename(self.open_file) if inside_dir else self.open_file
if result not in self.file_tabs:
new_tab = Tab(os.path.basename(result) if inside_dir else result)
new_tab.tooltip = str(new_tab.label)
setattr(new_tab, "file_path", result)
await tabs.add_tab(new_tab)
self.file_tabs[result] = new_tab
tabs.active = new_tab.id
else:
tabs.active = self.file_tabs[result].id
def action_open(self):
self.app.push_screen(FileOpen(), self.chose_file_to_open)
def action_find(self):
try:
self.query_one("#find-window")
return
except:
find_window = Window(
Vertical(
HorizontalGroup(
Input(placeholder="Find"),
Static("0 of 0", id="num-matches"),
Button("", flat=True),
Button("", flat=True),
),
HorizontalGroup(
Input(placeholder="Replace"),
),
),
icon="🔍",
start_open=True,
allow_resize=False,
allow_maximize=False,
id="find-window",
mode="temporary",
name="Find & Replace"
)
self.mount(find_window)
async def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected):
if self.open_file == str(event.path):
return
self.file_clicked = True
self.switching = True
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file not in self.unsaved_files:
if self.open_file:
self.file_tabs.pop(self.open_file)
tabs.remove_tab(tabs.active_tab)
self.open_file = str(event.path)
self.sub_title = os.path.basename(self.open_file)
if str(event.path) not in self.file_tabs:
new_tab = Tab(os.path.basename(str(event.path)))
new_tab.tooltip = str(new_tab.label)
setattr(new_tab, "file_path", str(event.path))
await tabs.add_tab(new_tab)
self.file_tabs[str(event.path)] = new_tab
tabs.active = new_tab.id
else:
tabs.active = self.file_tabs[str(event.path)].id
@on(Tabs.TabActivated)
def on_tab_shown(self, event: Tabs.TabActivated):
if self.file_clicked:
self.file_clicked = False
else:
self.open_file = getattr(event.tab, "file_path")
self.switching = True
code_editor: TextArea = self.query_one("#code-editor")
if self.open_file:
try:
f = open(self.open_file, "r", encoding="utf-8")
except Exception as e:
self.notify(f"Failed to open the file: {e}")
return
try:
if self.open_file in self.unsaved_files:
code_editor.text = self.unsaved_files[self.open_file]["current"]
else:
code_editor.text = f.read()
file_extension = self.open_file
dot_count = file_extension.count(".")
if dot_count == 1:
if file_extension.startswith("."):
file_extension = file_extension.removeprefix(".")
else:
file_extension = file_extension.rsplit(".", 1)[1]
elif dot_count > 1:
file_extension = file_extension.rsplit(".", 1)[1]
code_editor.language = theme_mappings.get(file_extension, None)
code_editor.disabled = False
except UnicodeDecodeError:
code_editor.text = "This file is in binary, it can't be opened. Sorry."
code_editor.language = None
code_editor.disabled = True
f.close()
else:
code_editor.text = ""
code_editor.language = None
code_editor.disabled = False
code_editor.focus()
CSS_PATH = "assets/style.tcss"
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):
super().__init__()
self.path = path
self.config_handler = ConfigHandler(self)
def compose(self) -> ComposeResult:
yield Header()
with Vertical(id="sidebar"):
with HorizontalGroup(id="sidebar-buttons"):
yield Button("")
yield Button("")
with ContentSwitcher(initial="files", id="sidebar-switcher"):
with Vertical(id="files"):
yield Static("EXPLORER")
yield CustomDirectoryTree(self.path, id="directory")
with Vertical(id="editor"):
first_tab = Tab("New File")
first_tab.file_path = None
yield Tabs(
first_tab,
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"))))
#if os.name == "nt":
#with Vertical(id="console-container"):
# yield RichLog(id="console")
# yield Input(placeholder="> ", id="console-input")
#else:
# yield Terminal(command="bash", id="terminal")
yield Footer()
if bool(int(self.config_handler.get("plugins", "enabled"))) == True:
yield PluginLoader()
def action_settings(self):
self.push_screen(SettingsScreen())
def get_system_commands(self, screen):
yield SystemCommand(
"Quit the application",
"Quit the application as soon as possible",
self.action_quit,
)
if screen.query("HelpPanel"):
yield SystemCommand(
"Hide keys and help panel",
"Hide the keys and widget help panel",
self.action_hide_help_panel,
)
else:
yield SystemCommand(
"Show keys and help panel",
"Show help for the focused widget and a summary of available keys",
self.action_show_help_panel,
)
yield SystemCommand("Settings", "Open the settings menu", self.action_settings)
async def chose_file_to_open(self, result):
if result == None: return
result = str(result)
if self.open_file == result:
return
def is_within_directory(file_path: str, directory: str) -> bool:
file_path = Path(file_path).resolve()
directory = Path(directory).resolve()
return directory in file_path.parents
self.switching = True
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file not in self.unsaved_files:
if self.open_file:
self.file_tabs.pop(self.open_file)
tabs.remove_tab(tabs.active_tab)
self.open_file = result
inside_dir = is_within_directory(result, self.path)
self.sub_title = os.path.basename(self.open_file) if inside_dir else self.open_file
if result not in self.file_tabs:
new_tab = Tab(os.path.basename(result) if inside_dir else result)
new_tab.tooltip = str(new_tab.label)
setattr(new_tab, "file_path", result)
await tabs.add_tab(new_tab)
self.file_tabs[result] = new_tab
tabs.active = new_tab.id
else:
tabs.active = self.file_tabs[result].id
def action_open(self):
self.app.push_screen(FileOpen(), self.chose_file_to_open)
def action_find(self):
try:
self.query_one("#find-window")
return
except:
find_window = Window(
Vertical(
HorizontalGroup(
Input(placeholder="Find"),
Static("0 of 0", id="num-matches"),
Button("", flat=True),
Button("", flat=True),
),
HorizontalGroup(
Input(placeholder="Replace"),
),
),
icon="🔍",
start_open=True,
allow_resize=False,
allow_maximize=False,
id="find-window",
mode="temporary",
name="Find & Replace"
)
self.mount(find_window)
async def on_directory_tree_file_selected(self, event: DirectoryTree.FileSelected):
if self.open_file == str(event.path):
return
self.file_clicked = True
self.switching = True
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file not in self.unsaved_files:
if self.open_file:
self.file_tabs.pop(self.open_file)
tabs.remove_tab(tabs.active_tab)
self.open_file = str(event.path)
self.sub_title = os.path.basename(self.open_file)
if str(event.path) not in self.file_tabs:
new_tab = Tab(os.path.basename(str(event.path)))
new_tab.tooltip = str(new_tab.label)
setattr(new_tab, "file_path", str(event.path))
await tabs.add_tab(new_tab)
self.file_tabs[str(event.path)] = new_tab
tabs.active = new_tab.id
else:
tabs.active = self.file_tabs[str(event.path)].id
def open_text_editor(self, file_content: bytes, file_extension: str):
code_editor: TextArea = self.query_one("#code-editor")
code_editor.language = theme_mappings.get(file_extension, None)
try:
code_editor.text = file_content.decode("utf-8")
code_editor.disabled = False
except UnicodeDecodeError:
code_editor.text = "This file is in binary, it can't be opened. Sorry."
code_editor.language = None
code_editor.disabled = True
code_editor.focus()
@on(Window.Minimized)
def window_minimized(self, event: Window.Minimized):
event.window.remove_window()
def open_editor(self, file_extension: str, file_content: str):
default_editors: dict = eval(self.config_handler.get("editor", "default_editors"))
editor_for_file = default_editors.get(file_extension, None)
if not editor_for_file:
self.open_text_editor(file_content, file_extension)
@on(Tabs.TabActivated)
def on_tab_shown(self, event: Tabs.TabActivated):
if self.file_clicked:
self.file_clicked = False
else:
self.open_file = getattr(event.tab, "file_path")
self.switching = True
content = b""
file_extension = ""
if self.open_file:
try:
f = open(self.open_file, "rb")
except Exception as e:
self.notify(f"Failed to open the file: {e}")
return
def on_text_area_changed(self, event: TextArea.Changed):
if event.text_area.id != "code-editor":
return
if self.switching:
self.switching = False
return
# if the file is unsaved, use the content we wrote into it
# otherwise, read the CURRENT state of the file
if self.open_file in self.unsaved_files:
content = self.unsaved_files[self.open_file]["current"]
else:
content = f.read()
# get the file extension
file_extension = self.open_file.split(".")[-1] if "." in self.open_file else ""
# close the file
f.close()
self.open_editor(file_extension, content)
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file:
if not self.open_file in self.unsaved_files:
with open(self.open_file, "r", encoding="utf-8") as f:
if f.read() == event.text_area.text: # TODO: figure out why im guetting what seems like a race conidition which is making this if statement needed
return
self.unsaved_files[self.open_file] = {"current": event.text_area.text, "original": f.read()}
@on(Window.Minimized)
def window_minimized(self, event: Window.Minimized):
event.window.remove_window()
tabs.active_tab.tooltip = f"Unsaved changes in {tabs.active_tab.label}"
tabs.active_tab.label = "[d orange]●[/] " + str(tabs.active_tab.label)
def on_text_area_changed(self, event: TextArea.Changed):
if event.text_area.id != "code-editor":
return
if self.switching:
self.switching = False
return
else:
self.unsaved_files[self.open_file]["current"] = event.text_area.text
if self.unsaved_files[self.open_file]["original"] == self.unsaved_files[self.open_file]["current"]:
tabs.active_tab.label = os.path.basename(self.open_file)
tabs.active_tab.tooltip = str(tabs.active_tab.label)
self.unsaved_files.pop(self.open_file)
def action_new(self):
tabs: Tabs = self.query_one("#file-tabs")
new_tab = Tab("New File")
setattr(new_tab, "file_path", None)
tabs.add_tab(new_tab)
tabs.active = new_tab.id
self.open_file = None
self.switching = True
code_editor: TextArea = self.query_one("#code-editor")
tabs: Tabs = self.query_one("#file-tabs")
if self.open_file:
if not self.open_file in self.unsaved_files:
with open(self.open_file, "r", encoding="utf-8") as f:
if f.read() == event.text_area.text: # TODO: figure out why im guetting what seems like a race conidition which is making this if statement needed
return
self.unsaved_files[self.open_file] = {"current": event.text_area.text, "original": f.read()}
code_editor.disabled = False
code_editor.text = ""
def done_saving(self, result):
if result is None: return
tabs.active_tab.tooltip = f"Unsaved changes in {tabs.active_tab.label}"
tabs.active_tab.label = "[d orange]●[/] " + str(tabs.active_tab.label)
with open(result, "w", encoding="utf-8") as f:
f.write(self.query_one("#code-editor").text)
else:
self.unsaved_files[self.open_file]["current"] = event.text_area.text
if self.unsaved_files[self.open_file]["original"] == self.unsaved_files[self.open_file]["current"]:
tabs.active_tab.label = os.path.basename(self.open_file)
tabs.active_tab.tooltip = str(tabs.active_tab.label)
self.unsaved_files.pop(self.open_file)
tabs: Tabs = self.query_one("#file-tabs")
tabs.active_tab.label = os.path.basename(result)
self.notify(f"Saved to {result} successfully.", title="Done!", markup=False)
self.query_one(DirectoryTree).reload()
def action_save_as(self):
self.push_screen(FileSave(), callback=self.done_saving)
def action_new(self):
tabs: Tabs = self.query_one("#file-tabs")
new_tab = Tab("New File")
setattr(new_tab, "file_path", None)
tabs.add_tab(new_tab)
tabs.active = new_tab.id
def action_save(self):
if self.open_file == None and self.query_one("#code-editor").disabled == False:
self.action_save_as()
self.open_file = None
self.switching = True
code_editor: TextArea = self.query_one("#code-editor")
# dont bother saving if there are no new changes
if not self.open_file in self.unsaved_files: return
with open(self.open_file, "w") as f:
f.write(self.unsaved_files[self.open_file]["current"])
tabs: Tabs = self.query_one("#file-tabs")
tabs.active_tab.label = os.path.basename(self.open_file)
tabs.active_tab.tooltip = str(tabs.active_tab.label)
self.unsaved_files.pop(self.open_file)
self.notify("Saved.")
def action_quit(self):
self.observer.stop()
self.observer.join()
return super().action_quit()
def on_ready(self):
# src/main.py: Tab<>
self.file_tabs = {}
self.open_file = None
self.unsaved_files = {} # list of paths
self.switching = False
self.file_clicked = False
code_editor.disabled = False
code_editor.text = ""
self.observer = Observer()
self.observer.schedule(Watcher(self), path=self.path)
self.observer.start()
def done_saving(self, result):
if result is None: return
self.config_handler.apply_settings()
with open(result, "w", encoding="utf-8") as f:
f.write(self.query_one("#code-editor").text)
tabs: Tabs = self.query_one("#file-tabs")
tabs.active_tab.label = os.path.basename(result)
self.notify(f"Saved to {result} successfully.", title="Done!", markup=False)
self.query_one(DirectoryTree).reload()
def action_save_as(self):
self.push_screen(FileSave(), callback=self.done_saving)
def action_save(self):
if self.open_file == None and self.query_one("#code-editor").disabled == False:
self.action_save_as()
# dont bother saving if there are no new changes
if not self.open_file in self.unsaved_files: return
with open(self.open_file, "w") as f:
f.write(self.unsaved_files[self.open_file]["current"])
tabs: Tabs = self.query_one("#file-tabs")
tabs.active_tab.label = os.path.basename(self.open_file)
tabs.active_tab.tooltip = str(tabs.active_tab.label)
self.unsaved_files.pop(self.open_file)
self.notify("Saved.")
def action_quit(self):
self.observer.stop()
self.observer.join()
return super().action_quit()
def on_ready(self):
# src/main.py: Tab<>
self.file_tabs = {}
self.open_file = None
self.unsaved_files = {} # list of paths
self.switching = False
self.file_clicked = False
self.observer = Observer()
self.observer.schedule(Watcher(self), path=self.path)
self.observer.start()
self.config_handler.apply_settings()
if __name__ == "__main__":
working_path = os.getcwd() if len(sys.argv) == 1 else sys.argv[1]
app = Berry(working_path)
app.run()
working_path = os.getcwd() if len(sys.argv) == 1 else sys.argv[1]
app = Berry(working_path)
app.run()

View File

@@ -71,7 +71,14 @@ class PluginLoader(Window):
setattr(self.app, f"action_{action_name}", wrapper)
def create_widget(self, widget_type: str, *args):
return getattr(textual.widgets, widget_type)(*args)
new_widget = getattr(textual.widgets, widget_type)(*args)
def on_func(event_name: str, function):
self.event_handler.subscribe(new_widget, event_name, function)
new_widget.message_signal.subscribe(self, lambda e: self.event_handler.message_handler(new_widget, e))
setattr(new_widget, "on", on_func)
return new_widget
def run_on_message(self, widget, event, function):
self.event_handler.subscribe(widget, event, function)

View File

@@ -1,14 +0,0 @@
local plugin = {}
function plugin.run()
--berry.config.defineSetting("my plugin", "test setting", "description", "boolean", "1")
button = berry.ui.button("Click me!", "success")
berry.ui.mount(button)
berry.ui.onMessage(button, "Pressed", function()
print("hi")
end)
end
return plugin

View File

@@ -0,0 +1,7 @@
local plugin = {}
function plugin.run()
berry.ui.notify("hi lmao")
end
return plugin

View File

@@ -1,5 +1,5 @@
{
"name": "Git",
"name": "Markdown Reader",
"author": "SpookyDervish",
"version": "1.0.0",
"dependencies": []

125
prompt.py
View File

@@ -1,80 +1,85 @@
from textual.screen import ModalScreen
from textual.containers import Vertical
from textual.widgets import Static, Button, Input
from textual.widgets import Static, Button, Input, Select
from textual.containers import HorizontalGroup, Center
from textual.binding import Binding
from typing import Literal
class Prompt(ModalScreen):
DEFAULT_CSS = """
Prompt {
align: center middle;
DEFAULT_CSS = """
Prompt {
align: center middle;
#window {
max-width: 50;
height: auto;
border: panel $accent;
#window {
max-width: 50;
height: auto;
border: panel $accent;
#question {
width: 100%;
margin: 2;
margin-top: 1;
}
#question {
width: 100%;
margin: 2;
margin-top: 1;
}
#bottom {
dock: bottom;
margin-bottom: 1;
margin-left: 1;
#bottom {
dock: bottom;
margin-bottom: 1;
margin-left: 1;
#yes {
margin-left: 1;
margin-right: 2;
}
#yes {
margin-left: 1;
margin-right: 2;
}
#confirm-string {
margin-left: 1;
}
#confirm-string {
margin-left: 1;
}
Input {
max-width: 25;
}
}
}
}
"""
Input {
max-width: 25;
}
}
}
}
"""
BINDINGS = [
Binding("escape", "close", "Close")
]
BINDINGS = [
Binding("escape", "close", "Close")
]
def action_close(self):
self.dismiss(None)
def action_close(self):
self.dismiss(None)
def on_button_pressed(self, event: Button.Pressed):
if event.button.id == "yes":
self.dismiss(True)
elif event.button.id == "no":
self.dismiss(False)
elif event.button.id == "confirm-string":
self.dismiss(self.query_one("#input").value)
def on_button_pressed(self, event: Button.Pressed):
if event.button.id == "yes":
self.dismiss(True)
elif event.button.id == "no":
self.dismiss(False)
elif event.button.id == "confirm-string":
self.dismiss(self.query_one("#input").value)
elif event.button.id == "confirm-list":
self.dismiss(self.query_one("#select").value)
def __init__(self, question: str, prompt_type: Literal["confirm", "string"], title: str = "Confirm"):
super().__init__()
self.question = question
self.window_title = title
self.prompt_type = prompt_type
def __init__(self, question: str, prompt_type: Literal["confirm", "string"], title: str = "Confirm", **kwargs):
super().__init__()
self.question = question
self.window_title = title
self.prompt_type = prompt_type
self.kwargs = kwargs
def compose(self):
with Vertical(id="window") as window:
window.border_title = self.window_title
yield Static(self.question, id="question")
def compose(self):
with Vertical(id="window") as window:
window.border_title = self.window_title
yield Static(self.question, id="question")
if self.prompt_type == "confirm":
with HorizontalGroup(id="bottom"):
yield Button("Yes", variant="success", id="yes")
yield Button("No", variant="error", id="no")
elif self.prompt_type == "string":
with HorizontalGroup(id="bottom"):
yield Input(id="input", max_length=30)
yield Button("Confirm", variant="success", id="confirm-string")
with HorizontalGroup(id="bottom"):
if self.prompt_type == "confirm":
yield Button("Yes", variant="success", id="yes")
yield Button("No", variant="error", id="no")
elif self.prompt_type == "string":
yield Input(id="input", **self.kwargs)
yield Button("Confirm", variant="success", id="confirm-string")
elif self.prompt_type == "list":
yield Select.from_values(**self.kwargs, id="select")
yield Button("Confirm", variant="success", id="confirm-list")

View File

@@ -93,7 +93,8 @@ class ConfigHandler:
}
self.config["editor"] = {
"word_wrap": "0",
"line_numbers": "1"
"line_numbers": "1",
"default_editors": {}
}
self.config["plugins"] = {
"enabled": "1",

1
test.py Normal file
View File

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