Moved plugins and refactor command system

- Moved plugins to apropriate sub folders
- Refactor command system with new add_command method and rename GetCommandSystemEvent to GetNewCommandSystemEvent
- Add RegisterCommandEvent for dynamic command registration
- Change footer container orientation to VERTICAL
- Add search-highlight tag to source buffer
- Add file change detection (deleted, externally modified) in source_file
- Add JSON prettify option to source view popup menu
- Enable hexpand on VTE widget
- Update plugins_controller_mixin to use widget_registry
This commit is contained in:
2026-02-18 23:45:07 -06:00
parent 69c8418a72
commit 6714053776
51 changed files with 176 additions and 68 deletions

View File

@@ -26,7 +26,7 @@ class FooterContainer(Gtk.Box):
self.ctx = self.get_style_context()
self.ctx.add_class("footer-container")
self.set_orientation(Gtk.Orientation.HORIZONTAL)
self.set_orientation(Gtk.Orientation.VERTICAL)
self.set_hexpand(True)
def _setup_signals(self):

View File

@@ -37,6 +37,9 @@ class CommandSystem:
method = getattr(commands, command)
return method.execute(*args)
def add_command(self, command_name: str, command: callable):
setattr(commands, command_name, command)
def emit(self, event: Code_Event_Types.CodeEvent):
""" Monkey patch 'emit' from command controller... """

View File

@@ -9,7 +9,7 @@ __all__ = []
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):
module = importlib.import_module(f"{__name__}.{module_name}")
globals()[module_name] = module # Add module to package namespace
# globals()[module_name] = module # Add module to package namespace
__all__.append(module_name)
del pkgutil

View File

@@ -18,12 +18,7 @@ def execute(
uri: str
):
logger.debug("Command: DnD Load File To Buffer")
file = view.command.get_file(view)
buffer = file.buffer
if not file.ftype == "buffer":
file = view.command.new_file(view)
file = view.command.new_file(view)
gfile = Gio.File.new_for_uri(uri)
view.command.exec_with_args(
@@ -31,4 +26,5 @@ def execute(
(view, gfile, file)
)
view.set_buffer(file.buffer)
update_info_bar_if_focused(view.command, view)

View File

@@ -17,10 +17,10 @@ class CommandsController(ControllerBase, list):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.GetCommandSystemEvent):
event.response = self.get_command_system()
if isinstance(event, Code_Event_Types.GetNewCommandSystemEvent):
event.response = self.get_new_command_system()
def get_command_system(self):
def get_new_command_system(self):
command_system = CommandSystem()
command_system.emit = self.emit
command_system.emit_to = self.emit_to

View File

@@ -27,6 +27,8 @@ class SourceViewsController(ControllerBase, list):
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.RemovedFileEvent):
self._remove_file(event)
elif isinstance(event, Code_Event_Types.RegisterCommandEvent):
self. _register_command(event)
if not self.signal_mapper.active_view: return
@@ -40,8 +42,22 @@ class SourceViewsController(ControllerBase, list):
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
self.signal_mapper.insert_text(event.file, event.text)
def _register_command(self, event: Code_Event_Types.RegisterCommandEvent):
self.state_manager.key_mapper.map_command(
event.command_name,
{
f"{event.binding_mode}": event.binding
}
)
for view in self:
view.command.add_command(
event.command_name,
event.command
)
def _get_command_system(self):
event = Event_Factory.create_event("get_command_system")
event = Event_Factory.create_event("get_new_command_system")
self.message_to("commands", event)
command = event.response

View File

@@ -10,6 +10,7 @@ from ...key_mapper import KeyMapper
from .states import *
class SourceViewStateManager:
def __init__(self):
self.key_mapper: KeyMapper = KeyMapper()

View File

@@ -70,29 +70,31 @@ class KeyMapper:
with open(bindings_file, 'r') as f:
data = json.load(f)["keybindings"]
for command in data:
press_state = "held" if "held" in data[command] else "released"
keyname = data[command][press_state]
state = NoKeyState
if "<Control>" in keyname:
state = state | CtrlKeyState
if "<Shift>" in keyname:
state = state | ShiftKeyState
if "<Alt>" in keyname:
state = state | AltKeyState
keyname = keyname.replace("<Control>", "") \
.replace("<Shift>", "") \
.replace("<Alt>", "") \
.lower()
getattr(self.states[state], press_state)[keyname] = command
self.map_command( command, data[command] )
def re_map(self):
self.states = copy.deepcopy(self._map)
def map_command(self, command, entry):
press_state = "held" if "held" in entry else "released"
keyname = entry[press_state]
state = NoKeyState
if "<Control>" in keyname:
state = state | CtrlKeyState
if "<Shift>" in keyname:
state = state | ShiftKeyState
if "<Alt>" in keyname:
state = state | AltKeyState
keyname = keyname.replace("<Control>", "") \
.replace("<Shift>", "") \
.replace("<Alt>", "") \
.lower()
getattr(self.states[state], press_state)[keyname] = command
def _key_press_event(self, eve):
keyname = Gdk.keyval_name(eve.keyval).lower()

View File

@@ -13,9 +13,14 @@ class SourceBuffer(GtkSource.Buffer):
def __init__(self):
super(SourceBuffer, self).__init__()
self._handler_ids = []
self.is_processing_completion: bool = False
self._handler_ids = []
self.create_tag(
"search-highlight",
background = "yellow",
foreground = "black"
)
def set_signals(

View File

@@ -43,10 +43,26 @@ class SourceFile(GtkSource.File):
)
def _changed(self, buffer: SourceBuffer):
self.check_file_on_disk()
event = Event_Factory.create_event("text_changed", buffer = buffer)
event.file = self
self.emit(event)
if self.is_deleted():
print("deleted")
# event = Event_Factory.create_event("file_deleted", buffer = buffer)
# event.file = self
# self.emit(event)
return
if self.is_externally_modified():
print("is_externally_modified")
# event = Event_Factory.create_event("file_externally_modified", buffer = buffer)
# event.file = self
# self.emit(event)
return
def _insert_text(
self,
buffer: SourceBuffer,

View File

@@ -59,6 +59,7 @@ class SourceView(GtkSource.View, SourceViewDnDMixin):
def _setup_signals(self):
self.connect("drag-data-received", self._on_drag_data_received)
self.connect("populate-popup", self._on_populate_popup)
def _subscribe_to_events(self):
...
@@ -76,6 +77,37 @@ class SourceView(GtkSource.View, SourceViewDnDMixin):
self._set_up_dnd()
def _on_populate_popup(self, view, menu):
buffer = self.get_buffer()
language = buffer.get_language()
if language.get_id() == "json":
self._load_prettify_json(view, menu)
menu.show_all()
def _load_prettify_json(self, view, menu):
menu.append( Gtk.SeparatorMenuItem() )
def on_prettify_json(menuitem):
import json
buffer = self.get_buffer()
start_itr, \
end_itr = buffer.get_start_iter(), buffer.get_end_iter()
data = buffer.get_text(start_itr, end_itr, False)
text = json.dumps(json.loads(data), separators = (',', ':'), indent = 4)
buffer.begin_user_action()
buffer.delete(start_itr, end_itr)
buffer.insert(start_itr, text)
buffer.end_user_action()
item = Gtk.MenuItem(label = "Prettify JSON")
item.connect("activate", on_prettify_json)
menu.append(item)
def clear_temp_cut_buffer_delayed(self):
if self._cut_temp_timeout_id:
GLib.source_remove(self._cut_temp_timeout_id)

View File

@@ -45,6 +45,7 @@ class VteWidget(Vte.Terminal):
ctx.add_class("vte-widget")
self.set_clear_background(False)
self.set_hexpand(True)
self.set_enable_sixel(True)
self.set_cursor_shape( Vte.CursorShape.IBEAM )

View File

@@ -5,8 +5,9 @@
from .code_event import CodeEvent
from .register_provider_event import RegisterProviderEvent
from .register_command_event import RegisterCommandEvent
from .get_command_system_event import GetCommandSystemEvent
from .get_new_command_system_event import GetNewCommandSystemEvent
from .request_completion_event import RequestCompletionEvent
from .cursor_moved_event import CursorMovedEvent
from .modified_changed_event import ModifiedChangedEvent

View File

@@ -9,5 +9,5 @@ from .code_event import CodeEvent
@dataclass
class GetCommandSystemEvent(CodeEvent):
class GetNewCommandSystemEvent(CodeEvent):
...

View File

@@ -0,0 +1,20 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
import gi
gi.require_version('GtkSource', '4')
from gi.repository import GtkSource
# Application imports
from ..base_event import BaseEvent
@dataclass
class RegisterCommandEvent(BaseEvent):
command_name: str = ""
command: callable = None
binding_mode: str = ""
binding: str = ""

View File

@@ -14,10 +14,7 @@ class InvalidPluginException(Exception):
class PluginsControllerMixin:
def requests_ui_element(self, target_id: str):
builder = settings_manager.get_builder()
ui_target = builder.get_object(target_id)
if not target_id in widget_registery.objects:
raise InvalidPluginException('Unknown UI "target_id" given in requests.')
if not ui_target:
raise InvalidPluginException('Unknown "target_id" given in requests.')
return ui_target
return widget_registery.objects[target_id]