started work on right click menus
This commit is contained in:
119
context_menu.py
Normal file
119
context_menu.py
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# totally not stolen from my code for my chat app Portal ;)
|
||||||
|
from __future__ import annotations
|
||||||
|
from textual.screen import ModalScreen
|
||||||
|
from textual.containers import Container
|
||||||
|
from textual.widgets import Static
|
||||||
|
from textual.geometry import Offset
|
||||||
|
from textual.message import Message
|
||||||
|
from textual.visual import VisualType
|
||||||
|
from textual import on, events
|
||||||
|
|
||||||
|
|
||||||
|
class NoSelectStatic(Static):
|
||||||
|
"""This class is used in window.py and windowbar.py to create buttons."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def allow_select(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ButtonStatic(NoSelectStatic):
|
||||||
|
"""This class is used in window.py, windowbar.py, and switcher.py to create buttons."""
|
||||||
|
|
||||||
|
class Pressed(Message):
|
||||||
|
def __init__(self, button: ButtonStatic) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.button = button
|
||||||
|
|
||||||
|
@property
|
||||||
|
def control(self) -> ButtonStatic:
|
||||||
|
return self.button
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
content: VisualType = "",
|
||||||
|
*,
|
||||||
|
expand: bool = False,
|
||||||
|
shrink: bool = False,
|
||||||
|
markup: bool = True,
|
||||||
|
name: str | None = None,
|
||||||
|
id: str | None = None,
|
||||||
|
classes: str | None = None,
|
||||||
|
disabled: bool = False,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
content=content,
|
||||||
|
expand=expand,
|
||||||
|
shrink=shrink,
|
||||||
|
markup=markup,
|
||||||
|
name=name,
|
||||||
|
id=id,
|
||||||
|
classes=classes,
|
||||||
|
disabled=disabled,
|
||||||
|
)
|
||||||
|
self.click_started_on: bool = False
|
||||||
|
|
||||||
|
def on_mouse_down(self, event: events.MouseDown) -> None:
|
||||||
|
|
||||||
|
self.add_class("pressed")
|
||||||
|
self.click_started_on = True
|
||||||
|
|
||||||
|
def on_mouse_up(self, event: events.MouseUp) -> None:
|
||||||
|
|
||||||
|
self.remove_class("pressed")
|
||||||
|
if self.click_started_on:
|
||||||
|
self.post_message(self.Pressed(self))
|
||||||
|
self.click_started_on = False
|
||||||
|
|
||||||
|
def on_leave(self, event: events.Leave) -> None:
|
||||||
|
|
||||||
|
self.remove_class("pressed")
|
||||||
|
self.click_started_on = False
|
||||||
|
|
||||||
|
|
||||||
|
class ContextMenu(ModalScreen):
|
||||||
|
DEFAULT_CSS = """
|
||||||
|
ContextMenu {
|
||||||
|
background: $background 0%;
|
||||||
|
align: left top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu_container {
|
||||||
|
padding: 0 1;
|
||||||
|
background: $surface;
|
||||||
|
width: 21;
|
||||||
|
border: hkey $panel;
|
||||||
|
& > ButtonStatic {
|
||||||
|
content-align: left middle;
|
||||||
|
&:hover { background: $panel-lighten-2; }
|
||||||
|
&.pressed { background: $primary; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, options: list[str], offset: Offset):
|
||||||
|
super().__init__()
|
||||||
|
self.options = options
|
||||||
|
self.mouse_offset = offset
|
||||||
|
|
||||||
|
def on_mouse_up(self, event: events.MouseUp):
|
||||||
|
if not self.query_one("#menu_container").region.contains(event.screen_x, event.screen_y):
|
||||||
|
self.dismiss(None)
|
||||||
|
|
||||||
|
@on(ButtonStatic.Pressed)
|
||||||
|
async def thingy(self, event: ButtonStatic.Pressed):
|
||||||
|
self.dismiss(event.button.content)
|
||||||
|
|
||||||
|
def compose(self):
|
||||||
|
with Container(id="menu_container"):
|
||||||
|
for option in self.options:
|
||||||
|
if isinstance(option, str):
|
||||||
|
yield ButtonStatic(option)
|
||||||
|
else:
|
||||||
|
yield option
|
||||||
|
|
||||||
|
def on_mount(self):
|
||||||
|
menu_container = self.query_one("#menu_container")
|
||||||
|
menu_container.styles.height = len(menu_container.children) + 2
|
||||||
|
menu_container.offset = self.mouse_offset
|
||||||
31
directory_tree_custom.py
Normal file
31
directory_tree_custom.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from textual.widgets import DirectoryTree, Rule
|
||||||
|
from textual.widgets.tree import TreeNode
|
||||||
|
from textual.events import MouseDown
|
||||||
|
from context_menu import ContextMenu, NoSelectStatic
|
||||||
|
|
||||||
|
|
||||||
|
class CustomDirectoryTree(DirectoryTree):
|
||||||
|
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):
|
||||||
|
self.right_clicked_node = None
|
||||||
|
|
||||||
|
def on_mouse_down(self, event: MouseDown):
|
||||||
|
if event.button != 3 or not "line" in event.style.meta:
|
||||||
|
return
|
||||||
|
selected_node = self.get_node_at_line(event.style.meta["line"])
|
||||||
|
self.right_clicked_node = selected_node
|
||||||
|
|
||||||
|
options = None
|
||||||
|
if self._safe_is_dir(self.right_clicked_node.data.path):
|
||||||
|
options = ["New Folder", "New File", NoSelectStatic(f'[d]{"-" * 17}[/]'), "Delete", "Rename", "Open"]
|
||||||
|
else:
|
||||||
|
options = ["Delete", "Rename", "Open"]
|
||||||
|
|
||||||
|
self.app.push_screen(ContextMenu(
|
||||||
|
[NoSelectStatic(f"[b]{self.right_clicked_node.label}[/]"), NoSelectStatic(f'[d]{"-" * 17}[/]')] + options,
|
||||||
|
event.screen_offset
|
||||||
|
), self.context_menu_chosen)
|
||||||
|
|
||||||
4
main.py
4
main.py
@@ -12,11 +12,11 @@ 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
|
||||||
from settings_store import ConfigHandler
|
from settings_store import ConfigHandler
|
||||||
|
from directory_tree_custom import CustomDirectoryTree
|
||||||
|
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ class Berry(App):
|
|||||||
with ContentSwitcher(initial="files", id="sidebar-switcher"):
|
with ContentSwitcher(initial="files", id="sidebar-switcher"):
|
||||||
with Vertical(id="files"):
|
with Vertical(id="files"):
|
||||||
yield Static("EXPLORER")
|
yield Static("EXPLORER")
|
||||||
yield DirectoryTree(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("New File")
|
||||||
|
|||||||
Reference in New Issue
Block a user