From d0369a18951ea439e2286e5bb4763afb5a999f44 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 19 Mar 2024 04:42:33 +0530 Subject: [PATCH] feat: Custom list widget for open editors --- biscuit/core/components/utils/__init__.py | 1 + .../core/components/utils/closelistitem.py | 90 +++++++++++++++++++ .../views/sidebar/explorer/open_editors.py | 67 ++++++-------- .../layout/base/content/editors/__init__.py | 4 + 4 files changed, 122 insertions(+), 40 deletions(-) create mode 100644 biscuit/core/components/utils/closelistitem.py diff --git a/biscuit/core/components/utils/__init__.py b/biscuit/core/components/utils/__init__.py index b0a5c42c..78c507bb 100644 --- a/biscuit/core/components/utils/__init__.py +++ b/biscuit/core/components/utils/__init__.py @@ -5,6 +5,7 @@ from .buttonsentry import ButtonsEntry from .callername import caller_name from .canvas import Canvas +from .closelistitem import CloseListItem from .codicon import get_codicon from .colorizer import colorize from .entry import Entry diff --git a/biscuit/core/components/utils/closelistitem.py b/biscuit/core/components/utils/closelistitem.py new file mode 100644 index 00000000..0fbeb9ca --- /dev/null +++ b/biscuit/core/components/utils/closelistitem.py @@ -0,0 +1,90 @@ +import tkinter as tk + +from biscuit.core.components.utils.iconbutton import IconButton + +from .codicon import get_codicon +from .frame import Frame +from .icon import Icon + + +class CloseListItem(Frame): + """ + A varient of IconLabelButton that has a close icon on the right side of the button. + """ + def __init__(self, master, text=None, icon=None, function=lambda *_: None, closefn=lambda *_:None, iconside=tk.LEFT, padx=5, pady=1, *args, **kwargs) -> None: + super().__init__(master, padx=padx, pady=pady, *args, **kwargs) + self.function = function + self.closefn = closefn + + self.bg, self.fg, self.hbg, self.hfg = self.base.theme.utils.iconlabelbutton.values() + self.config(bg=self.bg) + self.text = text + self.icon = icon + + if icon: + self.icon_label = tk.Label(self, text=get_codicon(self.icon), anchor=tk.CENTER, + bg=self.bg, fg=self.fg, font=("codicon", 14)) + self.icon_label.pack(side=iconside, fill=tk.BOTH, expand=True) + + if text: + self.text_label = tk.Label(self, text=self.text, anchor=tk.CENTER, pady=2, + bg=self.bg, fg=self.fg, font=("Segoe UI", 10)) + self.text_label.pack(side=iconside, fill=tk.BOTH, expand=True) + + self.close_btn = IconButton(self, 'close', self.closefn) + self.close_btn.config(bg=self.bg, fg=self.fg) + self.close_btn.pack(side=tk.RIGHT, fill=tk.BOTH) + + self.config_bindings() + self.visible = False + + def config_bindings(self) -> None: + self.bind("", self.on_enter) + self.bind("", self.on_leave) + + self.bind("", self.on_click) + if self.text: + self.text_label.bind("", self.on_click) + if self.icon: + self.icon_label.bind("", self.on_click) + + def on_enter(self, *_) -> None: + self.config(bg=self.hbg) + if self.text: + self.text_label.config(bg=self.hbg, fg=self.hfg) + if self.icon: + self.icon_label.config(bg=self.hbg, fg=self.hfg) + self.close_btn.config(bg=self.hbg, fg=self.hfg) + + def on_leave(self, *_) -> None: + self.config(bg=self.bg) + if self.text: + self.text_label.config(bg=self.bg, fg=self.fg) + if self.icon: + self.icon_label.config(bg=self.bg, fg=self.fg) + self.close_btn.config(bg=self.bg, fg=self.fg) + + def on_click(self, *_) -> None: + self.function() + + def change_text(self, text) -> None: + self.text_label.config(text=text) + + def change_icon(self, icon) -> None: + self.icon_label.config(text=icon) + + def set_pack_data(self, **kwargs) -> None: + self.pack_data = kwargs + + def get_pack_data(self): + return self.pack_data + + def show(self) -> None: + if not self.visible: + self.visible = True + self.pack(**self.get_pack_data()) + + def hide(self) -> None: + if self.visible: + self.visible = False + self.pack_forget() diff --git a/biscuit/core/components/views/sidebar/explorer/open_editors.py b/biscuit/core/components/views/sidebar/explorer/open_editors.py index 46359769..8f417053 100644 --- a/biscuit/core/components/views/sidebar/explorer/open_editors.py +++ b/biscuit/core/components/views/sidebar/explorer/open_editors.py @@ -1,14 +1,7 @@ -import os -import platform -import shutil -import subprocess -import threading import tkinter as tk -import pyperclip - from biscuit.core.components.floating.palette.actionset import ActionSet -from biscuit.core.components.utils import Tree +from biscuit.core.components.utils import CloseListItem, Menubutton from ..item import SidebarViewItem @@ -18,49 +11,43 @@ def __init__(self, master, startpath=None, itembar=True, *args, **kwargs) -> Non self.title = 'Open Editors' self.__buttons__ = (('new-file', lambda: self.base.palette.show('newfile:')),) super().__init__(master, itembar, *args, **kwargs) - - self.tree = Tree(self.content, startpath, singleclick=self.openfile, *args, **kwargs) - self.tree.bind("", self.adjust_frame_size) - self.tree.grid(row=0, column=0, sticky=tk.NSEW) - self.tree.bind("<>", self.openfile) - self.path = startpath + self.nodes = {} - def adjust_frame_size(self, *_): - if items := self.tree.get_children(): - frame_height = len(items) * 25 + 5 + def refresh(self): + if not self.nodes: + self.itembar.hide_content() else: - frame_height = 1 - self.tree.config(height=frame_height) + self.itembar.show_content() self.update() def add_item(self, editor): - self.tree.insert('', 'end', text=editor.filename, values=(editor.path, 'file')) - self.adjust_frame_size() + temp = CloseListItem(self.content, editor.filename, function=lambda p=editor.path: self.openfile(p), + closefn=lambda p=editor.path: self.closefile(p), padx=10) + temp.text_label.config(anchor=tk.W) + temp.pack(fill=tk.X, expand=True) + + self.nodes[editor.path] = temp + self.refresh() def remove_item(self, editor): - for child in self.tree.get_children(): - if self.tree.item_fullpath(child) == editor.path: - self.tree.delete(child) - self.adjust_frame_size() - if not len(self.base.editorsmanager.active_editors): - self.disable() - return + e = self.nodes.pop(editor.path) + e.pack_forget() + e.destroy() + self.refresh() def set_active(self, editor): - for child in self.tree.get_children(): - if self.tree.item_fullpath(child) == editor.path: - self.tree.tree.selection_set(child) - self.adjust_frame_size() - return + # TODO: set highlight, clear highlight on others + ... def clear(self): - self.tree.delete(*self.tree.get_children()) - self.adjust_frame_size() + for node in self.nodes.values(): + node.destroy() + self.nodes = {} - def openfile(self, _) -> None: - if self.tree.selected_type() != 'file': - return + def openfile(self, path) -> None: + self.base.editorsmanager.set_active_editor_by_path(path) - path = self.tree.selected_path() - self.base.open_editor(path) + def closefile(self, path) -> None: + self.base.editorsmanager.close_editor_by_path(path) + \ No newline at end of file diff --git a/biscuit/core/layout/base/content/editors/__init__.py b/biscuit/core/layout/base/content/editors/__init__.py index 81a83c34..a0037e28 100644 --- a/biscuit/core/layout/base/content/editors/__init__.py +++ b/biscuit/core/layout/base/content/editors/__init__.py @@ -140,6 +140,10 @@ def close_editor(self, editor: Editor) -> None: self.base.explorer.open_editors.remove_item(editor) self.refresh() + def close_editor_by_path(self, path: str) -> None: + "removes an editor by path, keeping it in cache." + self.close_editor(self.get_editor(path)) + def get_editor(self, path: str) -> Editor: "Get editor by path" for editor in self.active_editors: