Remove tabs UI from code editor and move to plugin. Enhance plugin system.

- Remove tabs controller, tab widget, and tabs widget files and move to plugin
- Delete plugins/README.txt
- Add register_controller method to controller system for plugin use
- Add error handling for plugin crashes via futures callback
This commit is contained in:
2026-02-26 21:09:00 -06:00
parent 597ac2b06a
commit b724d41f6c
16 changed files with 112 additions and 73 deletions

View File

@@ -0,0 +1,3 @@
"""
Pligin Module
"""

View File

@@ -0,0 +1,3 @@
"""
Pligin Package
"""

View File

@@ -0,0 +1,7 @@
{
"name": "Tabs Bar",
"author": "ITDominator",
"version": "0.0.1",
"support": "",
"requests": {}
}

View File

@@ -0,0 +1,32 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from plugins.plugin_types import PluginCode
from .tabs_controller import TabsController
class Plugin(PluginCode):
def __init__(self):
super(Plugin, self).__init__()
def _controller_message(self, event: Code_Event_Types.CodeEvent):
...
def load(self):
tabs_controller = TabsController()
code_container = self.requests_ui_element("code-container")
self.register_controller("tabs", tabs_controller)
code_container.add( tabs_controller.tabs_widget )
code_container.reorder_child(tabs_controller.tabs_widget, 0)
def run(self):
...

View File

@@ -0,0 +1,79 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class TabWidget(Gtk.Box):
"""docstring for TabWidget"""
def __init__(self):
super(TabWidget, self).__init__()
self.file = None
self._handler_id = None
self._eve_handler_id = None
self._setup_styling()
self._setup_signals()
self._load_widgets()
def _setup_styling(self):
ctx = self.get_style_context()
ctx.add_class("tab-widget")
self.set_orientation(0)
self.set_hexpand(False)
self.set_vexpand(False)
self.set_size_request(-1, 12)
def _setup_signals(self):
...
def _load_widgets(self):
self.event_box = Gtk.EventBox()
self.label = Gtk.Label()
self.close_bttn = Gtk.Button()
icon = Gtk.Image(stock = Gtk.STOCK_CLOSE)
self.event_box.set_above_child(True)
ctx = self.label.get_style_context()
ctx.add_class("tab-label")
ctx = self.close_bttn.get_style_context()
ctx.add_class("tab-close-bttn")
self.label.set_xalign(0.0)
self.label.set_margin_left(25)
self.label.set_margin_right(25)
self.label.set_hexpand(True)
self.close_bttn.add(icon)
self.event_box.add(self.label)
self.add(self.event_box)
self.add(self.close_bttn)
self.show_all()
def clear_signals_and_data(self):
self.close_bttn.disconnect(self._handler_id)
self.event_box.disconnect(self._eve_handler_id)
self._handler_id = None
for child in self.get_children():
child.unparent()
child.run_dispose()
child.destroy()
def set_close_signal(self, callback):
self._handler_id = self.close_bttn.connect(
'clicked',
callback,
self.file
)

View File

@@ -0,0 +1,75 @@
# Python imports
# Lib imports
import gi
from gi.repository import Gtk
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Code_Event_Types
from core.widgets.code.source_view import SourceView
from .tabs_widget import TabsWidget
from .tab_widget import TabWidget
class TabsController(ControllerBase):
def __init__(self):
super(TabsController, self).__init__()
self.tabs_widget: TabsWidget = TabsWidget()
self.tabs_widget.message = self.message
def _controller_message(self, event: Code_Event_Types.CodeEvent):
if isinstance(event, Code_Event_Types.FocusedViewEvent):
self.tabs_widget.view_changed( event.view.get_buffer() )
elif isinstance(event, Code_Event_Types.FilePathSetEvent):
self.update_tab_label(event)
elif isinstance(event, Code_Event_Types.ModifiedChangedEvent):
self.tabs_widget.modified_changed( event.buffer )
elif isinstance(event, Code_Event_Types.FileExternallyDeletedEvent):
self.tabs_widget.externally_deleted( event.buffer )
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
self.add_tab(event)
elif isinstance(event, Code_Event_Types.PoppedFileEvent):
...
elif isinstance(event, Code_Event_Types.RemovedFileEvent):
self.remove_tab(event)
def get_tabs_widget(self):
return self.tabs_widget
def update_tab_label(self, event: Code_Event_Types.FilePathSetEvent):
for page_widget in self.tabs_widget.get_children():
tab = self.tabs_widget.get_tab_label(page_widget)
if not event.file == tab.file: continue
tab.label.set_label(event.file.fname)
break
def add_tab(self, event: Code_Event_Types.AddedNewFileEvent):
tab = TabWidget()
tab.file = event.file
tab.label.set_label(event.file.fname)
self.tabs_widget.append_page(Gtk.Separator(), tab)
tab.show_all()
def remove_tab(self, event: Code_Event_Types.RemovedFileEvent):
for page_widget in self.tabs_widget.get_children():
tab = self.tabs_widget.get_tab_label(page_widget)
if not event.file == tab.file: continue
tab.clear_signals_and_data()
self.tabs_widget.remove_page(
self.tabs_widget.page_num(page_widget)
)
break

View File

@@ -0,0 +1,179 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from .tab_widget import TabWidget
class TabsWidget(Gtk.Notebook):
def __init__(self):
super(TabsWidget, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
self.set_scrollable(True)
def _setup_signals(self):
self.connect("page-added", self._page_added)
self.switch_page_id = \
self.connect_after("switch-page", self._switch_page)
def _subscribe_to_events(self):
...
def _load_widgets(self):
...
def _page_added(self, notebook, page_widget, page_num):
tab = self.get_tab_label(page_widget)
tab.set_close_signal(self._close_tab)
self._bind_tab_menu(tab, page_widget)
page_widget.show()
self.set_tab_detachable(page_widget, True)
self.set_tab_reorderable(page_widget, True)
def _close_tab(self, button, file):
event = Event_Factory.create_event(
"remove_file",
buffer = file.buffer
)
self.message(event)
def _switch_page(self, notebook, page_widget, page_num):
tab = self.get_tab_label(page_widget)
event = Event_Factory.create_event(
"set_active_file",
buffer = tab.file.buffer
)
self.message(event)
def _bind_tab_menu(self, tab, page_widget):
def do_context_menu(tab, eve, page_widget):
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
menu = self.create_menu(page_widget)
menu.popup_at_pointer(eve)
tab._eve_handler_id = \
tab.event_box.connect(
"button-release-event",
do_context_menu,
page_widget
)
def create_menu(self, page_widget) -> Gtk.Menu:
context_menu = Gtk.Menu()
close_item = Gtk.MenuItem(label = "Close Tab")
close_left_item = Gtk.MenuItem(label = "Close Tabs Left")
close_right_item = Gtk.MenuItem(label = "Close Tabs Right")
close_other_item = Gtk.MenuItem(label = "Close Other Tabs")
close_all_item = Gtk.MenuItem(label = "Close All Tabs")
close_item.connect("activate", self.close_item, page_widget)
close_left_item.connect("activate", self.close_left_items, page_widget)
close_right_item.connect("activate", self.close_right_items, page_widget)
close_other_item.connect("activate", self.close_other_items, page_widget)
close_all_item.connect("activate", self.close_all_items, page_widget)
context_menu.append(close_item)
context_menu.append(close_left_item)
context_menu.append(close_right_item)
context_menu.append(close_other_item)
context_menu.append(close_all_item)
context_menu.show_all()
return context_menu
def view_changed(self, buffer):
for page_widget in self.get_children():
tab = self.get_tab_label(page_widget)
if not buffer == tab.file.buffer: continue
self.handler_block(self.switch_page_id)
self.set_current_page(
self.page_num(page_widget)
)
self.handler_unblock(self.switch_page_id)
break
def modified_changed(self, buffer):
for page_widget in self.get_children():
tab = self.get_tab_label(page_widget)
if not buffer == tab.file.buffer: continue
ctx = tab.label.get_style_context()
ctx.remove_class("file-deleted")
if buffer.get_modified():
ctx.add_class("file-changed")
else:
ctx.remove_class("file-changed")
break
def externally_deleted(self, buffer):
for page_widget in self.get_children():
tab = self.get_tab_label(page_widget)
if not buffer == tab.file.buffer: continue
ctx = tab.label.get_style_context()
ctx.add_class("file-deleted")
break
def close_item(self, menu_item, page_widget):
tab = self.get_tab_label(page_widget)
tab.close_bttn.clicked()
def close_left_items(self, menu_item, page_widget):
children = self.get_children()
i = children.index(page_widget)
if i == 0: return
for widget in children[ : i]:
tab = self.get_tab_label(widget)
tab.close_bttn.clicked()
def close_right_items(self, menu_item, page_widget):
children = self.get_children()
i = children.index(page_widget) + 1
if i == len(children): return
for widget in children[i : ]:
tab = self.get_tab_label(widget)
tab.close_bttn.clicked()
def close_other_items(self, menu_item, page_widget):
self.close_left_items(menu_item, page_widget)
self.close_right_items(menu_item, page_widget)
def close_all_items(self, menu_item, page_widget):
children = self.get_children()
for widget in children[ : ]:
tab = self.get_tab_label(widget)
tab.close_bttn.clicked()