Skip to content

Commit

Permalink
Merge pull request #220 from billyeatcookies/ltaoist/main
Browse files Browse the repository at this point in the history
feat: Rename splitted classes (changes to project structure)
  • Loading branch information
tomlin7 authored Jan 28, 2024
2 parents 9a1f686 + 89b7b6f commit ec18442
Show file tree
Hide file tree
Showing 25 changed files with 502 additions and 369 deletions.
2 changes: 1 addition & 1 deletion biscuit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '2.58.0'
__version__ = '2.59.0'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])

# For tests to run successfully
Expand Down
357 changes: 27 additions & 330 deletions biscuit/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,326 +1,17 @@
import os
import platform
import sys
import threading
import tkinter as tk
from .config import ConfigManager
from .events import EventManager
from .gui import GUIManager

from .api import *
from .components import *
from .layout import *
from .settings import *
from .utils import *

class SetupManager:

def setup(self) -> None:
# flag that all components have been added
self.initialized = False

self.setup_configs()
self.setup_tk()
self.setup_floating_widgets()

grip_w = tk.Frame(self, bg=self.base.theme.primary_background, cursor='left_side')
grip_w.bind("<B1-Motion>", lambda e: self.resize('w'))
grip_w.pack(fill=tk.Y, side=tk.LEFT)

self.root = Root(self)
self.root.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)

grip_e = tk.Frame(self, bg=self.base.theme.primary_background, cursor='right_side')
grip_e.bind("<B1-Motion>", lambda e: self.resize('e'))
grip_e.pack(fill=tk.Y, side=tk.LEFT)

def setup_configs(self) -> None:
self.git_found = False
self.active_directory = None
self.active_branch_name = None
self.onupdate_functions = []
self.onfocus_functions = []

self.testing = False
if os.environ.get('ENVIRONMENT') == 'test':
self.testing = True

self.sysinfo = SysInfo(self)
self.settings = Settings(self)

self.configs = self.settings.config
self.theme = self.configs.theme

self.events = Events(self)
self.binder = Binder(self)
self.git = Git(self)
self.languageservermanager = LanguageServerManager(self)

def setup_tk(self) -> None:
if platform.system() == "Windows":
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)

self.overrideredirect(True)
self.update_idletasks()

GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
hwnd = windll.user32.GetParent(self.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
self.withdraw()
self.after(10, self.wm_deiconify)

self.dpi_value = self.winfo_fpixels('1i')
self.scale = self.dpi_value / 96.11191135734073
#self.tk.call('tk', 'scaling', self.scale)

self.min_width = round(500 * self.scale)
self.min_height = round(500 * self.scale)

app_width = round(1150 * self.scale)
app_height = round(700 * self.scale)
x = int((self.winfo_screenwidth() - app_width) / 2)
y = int((self.winfo_screenheight() - app_height) / 2)

self.geometry(f"{app_width}x{app_height}+{x}+{y}")

def setup_floating_widgets(self) -> None:
self.palette = Palette(self)
self.findreplace = FindReplace(self)
self.notifications = Notifications(self)

self.autocomplete = AutoComplete(self)
self.definitions = Definitions(self)
self.pathview = PathView(self)
self.hover = Hover(self)

def setup_path(self, appdir: str) -> None:
# setup all paths used across editor
self.appdir = os.path.dirname(appdir)
sys.path.append(self.appdir)

self.resdir = os.path.join(getattr(sys, "_MEIPASS", os.path.dirname(appdir)), "res")
self.configdir = os.path.join(self.appdir, "config")
self.extensionsdir = os.path.join(self.appdir, "extensions")

try:
# creates the extensions directory next to executable
os.makedirs(self.extensionsdir, exist_ok=True)
except Exception as e:
print(f"Extensions failed: {e}")

class LateSetupManager:

def late_setup(self) -> None:
# setup after initializing base gui
self.setup_references()
self.binder.late_bind_all()
self.editorsmanager.add_default_editors()
self.settings.late_setup()

self.focus_set()
self.setup_extensions()

def setup_references(self) -> None:
"References to various components of the editor"
self.menubar = self.root.menubar
self.statusbar = self.root.statusbar
self.editorsmanager = self.root.baseframe.contentpane.editorspane
self.panel = self.root.baseframe.contentpane.panel
self.terminalmanager = self.panel.terminal
self.contentpane = self.root.baseframe.contentpane
self.sidebar = self.root.baseframe.sidebar
self.explorer = self.sidebar.explorer
self.outline = self.sidebar.outline
self.source_control = self.sidebar.source_control
self.extensionsGUI = self.sidebar.extensions
self.logger = self.panel.logger

def setup_extensions(self) -> None:
if self.testing:
return

self.api = ExtensionsAPI(self)
self.extensionsmanager = ExtensionManager(self)
self.extensionsGUI.initialize()

class Misc:

def set_title(self, title: str=None) -> None:
if not self.initialized:
return
self.menubar.set_title(title)
self.menubar.reposition_title()

def open_directory(self, dir: str) -> None:
if not dir or not os.path.isdir(dir):
return

self.active_directory = dir
self.explorer.directory.change_path(dir)
self.set_title(os.path.basename(self.active_directory))

self.editorsmanager.delete_all_editors()
self.terminalmanager.delete_all_terminals()
self.terminalmanager.open_terminal()

self.git.check_git()
self.update_git()

def update_git(self) -> None:
self.statusbar.update_git_info()
self.source_control.refresh()


def clone_repo(self, url: str, dir: str) -> None:
try:
def clone() -> None:
repodir = self.git.clone(url, dir)
self.open_directory(repodir)

temp = threading.Thread(target=clone)
temp.daemon = True
temp.start()

except Exception as e:
self.base.logger.error(f"Cloning repository failed: {e}")
self.base.notifications.error("Cloning repository failed: see logs")
return

def close_active_directory(self) -> None:
self.active_directory = None
self.explorer.directory.close_directory()
self.editorsmanager.delete_all_editors()
self.set_title()
self.git_found = False
self.update_git()

def close_active_editor(self) -> None:
self.editorsmanager.close_active_editor()

def goto_location_in_active_editor(self, position: int) -> None:
if editor := self.editorsmanager.active_editor:
if editor.content and editor.content.editable:
editor.content.goto(position)

def goto_location(self, path: str, position: int) -> None:
if self.editorsmanager.is_open(path):
self.editorsmanager.set_active_editor_by_path(path).content.goto(position)
return

editor = self.open_editor(path, exists=True)
editor.bind("<<FileLoaded>>", lambda e: editor.content.goto(position))

def open_editor(self, path: str, exists: bool=True) -> Editor | BaseEditor:
if exists and not os.path.isfile(path):
return

return self.editorsmanager.open_editor(path, exists)

def open_diff(self, path: str, kind: str) -> None:
self.editorsmanager.open_diff_editor(path, kind) # type: ignore

def open_settings(self, *_) -> None:
self.editorsmanager.add_editor(SettingsEditor(self.editorsmanager))

def open_game(self, name: str) -> None:
self.editorsmanager.open_game(name)

def register_game(self, game: BaseGame) -> None:
#TODO game manager class
register_game(game)
self.settings.gen_actionset()

def register_langserver(self, language: str, command: str) -> None:
self.languageservermanager.register_langserver(language, command)


def open_in_new_window(self, dir: str) -> None:
#Process(target=App(sys.argv[0], dir).run).start()
self.notifications.show("Feature not available in this version.")

def open_new_window(self) -> None:
# Process(target=App(sys.argv[0]).run).start()
self.notifications.show("Feature not available in this version.")

def toggle_terminal(self) -> None:
self.panel.set_active_view(self.panel.terminal)
self.contentpane.toggle_panel()

def update_statusbar(self) -> None:
if editor := self.editorsmanager.active_editor:
if editor.content and editor.content.editable:
self.statusbar.toggle_editmode(True)
active_text = editor.content.text
self.statusbar.set_encoding(active_text.encoding)
return self.statusbar.set_line_col_info(
active_text.line, active_text.column, len(active_text.selection))

self.statusbar.toggle_editmode(False)

def register_onupdate(self, fn) -> None:
self.onupdate_functions.append(fn)

def on_gui_update(self, *_) -> None:
for fn in self.onupdate_functions:
try:
fn()
except tk.TclError:
pass

def register_onfocus(self, fn) -> None:
self.onfocus_functions.append(fn)

def on_focus(self, *_) -> None:
for fn in self.onfocus_functions:
try:
fn()
except tk.TclError:
pass


def resize(self, mode: str) -> None:
abs_x = self.winfo_pointerx() - self.winfo_rootx()
abs_y = self.winfo_pointery() - self.winfo_rooty()
width = self.winfo_width()
height= self.winfo_height()
x = self.winfo_rootx()
y = self.winfo_rooty()

match mode:
case 'e':
if height > self.min_height and abs_x > self.min_width:
return self.geometry(f"{abs_x}x{height}")
case 'n':
height = height - abs_y
y = y + abs_y
if height > self.min_height and width > self.min_width:
return self.geometry(f"{width}x{height}+{x}+{y}")
case 'w':
width = width - abs_x
x = x + abs_x
if height > self.min_height and width > self.min_width:
return self.geometry(f"{width}x{height}+{x}+{y}")
case 's':
height = height - (height - abs_y)
if height > self.min_height and width > self.min_width:
return self.geometry(f"{width}x{height}+{x}+{y}")

self.menubar.reposition_title()

class App \
(tk.Tk,
SetupManager,
LateSetupManager,
Misc):
class App(GUIManager, EventManager, ConfigManager):
"""
BISCUIT CORE
------------
Main point of having this class is to have a single point of access to all the important parts of the app. This class
holds reference to all the components of Biscuit and every class of biscuit have a reference back to this `base` class.
Manages App Configuration, GUI (Tkinter), Events of the App.
Single point of access to all the important parts of the app. Holds reference to all the components
of Biscuit and every class of biscuit have a reference back to this `base` class.
i.e. `self.base` is the reference to this class from any other class of biscuit.
Usage
Expand All @@ -343,16 +34,18 @@ def foo(self):
print(editor.path)
if (editor.content.editable):
self.base.notifications.info(":)")
Attributes
----------
appdir
the directory where the app executable is at
dir
optional argument to open a folder from cli
"""

def __init__(self, appdir: str="", dir: str="", *args, **kwargs) -> None:
"""
Parameters
----------
appdir
the directory where the app executable is at
dir
optional argument to open a folder from cli
"""

super().__init__(*args, **kwargs)
self.base = self
self.setup_path(appdir)
Expand All @@ -365,17 +58,21 @@ def run(self) -> None:
self.mainloop()

# after the gui mainloop ends, also stop the extension server
self.extensionsmanager.stop_server()
self.extensions_manager.stop_server()

def setup(self) -> None:
self.initialized = False

self.setup_configs()
self.setup_tk()

def initialize_editor(self, dir: str) -> None:
self.initialized = True

self.palette.generate_help_actionset()
self.logger.info('Initializing editor finished.')

self.update_idletasks()
self.initialized = True

self.menubar.update()
self.set_title()

self.open_directory(dir)

Loading

2 comments on commit ec18442

@ltaoist
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! I think the EventManager design is cool! And we advanced on a more clear lane。

@tomlin7
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops! I think the EventManager design is cool! And we advanced on a more clear lane。

definitely, your pr inspired me to update and clean up many parts of the app :)

Please sign in to comment.