Adding WIP files view widget

This commit is contained in:
2026-10-04 00:19:51 -05:00
parent ced86aa2b7
commit 6ceb929242
30 changed files with 802 additions and 6 deletions
+12 -3
View File
@@ -8,6 +8,7 @@ from gi.repository import Gtk
# Application imports
from .quad.quad_container import QuadContainer
from core.widgets.webkit.webkit_ui import WebkitUI
from core.widgets.files.files_base import FilesBase
@@ -45,21 +46,29 @@ class CenterContainer(Gtk.Box):
widget_registery.expose_object("center-container", self)
quad_container = QuadContainer()
files_base = FilesBase()
webkit_ui = WebkitUI()
button = Gtk.Button(label = "Click Me!")
label = Gtk.Label(label = "bright")
glade_box = widget_registery.get_object("glade_box")
files_widget = files_base.create_tabs_view()
button.connect("clicked", self._hello_world)
webkit_ui.load_context_base_path()
files_widget.create_new_tab("test")
files_widget.create_new_tab("test2")
files_widget.create_new_tab("test3")
files_widget.create_new_tab("test4")
files_widget.create_new_tab("test5")
button.show()
label.show()
glade_box.show()
quad_container.top_container.pack1(button, True, True)
quad_container.top_container.pack2(glade_box, True, True)
quad_container.bottom_container.pack1(webkit_ui, True, True)
quad_container.bottom_container.pack2(label, True, True)
quad_container.bottom_container.pack2(files_widget, True, True)
self.add(quad_container)
+3
View File
@@ -0,0 +1,3 @@
"""
Files Package
"""
@@ -0,0 +1,5 @@
"""
Code Command System Package
"""
from .files_view_command_system import FilesViewCommandSystem
@@ -0,0 +1,16 @@
"""
Code Commands Package
"""
import pkgutil
import importlib
__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
__all__.append(module_name)
del pkgutil
del importlib
@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: Get File Type")
@@ -0,0 +1,20 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: New File")
@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: Open File(s)")
@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: Save File")
@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: Save File As")
@@ -0,0 +1,19 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
def execute(
view: Gtk.Notebook,
*args,
**kwargs
):
logger.debug("Command: Update Info Bar")
@@ -0,0 +1,32 @@
# Python imports
# Lib imports
# Application imports
from libs.event_factory import Event_Factory, Files_Event_Types
from libs.command_system import CommandSystem
from ..tabs_view import TabsView
from . import commands
class FilesViewCommandSystem(CommandSystem):
def __init__(self):
super(FilesViewCommandSystem, self).__init__(commands)
def toggle_plugins_ui(self):
event = Event_Factory.create_event( "toggle_plugins_ui" )
self.emit_to("plugins", event)
def emit(self, event: Files_Event_Types.FilesEvent):
""" Monkey patch 'emit' from command controller... """
...
def emit_to(self, controller: str, event: Files_Event_Types.FilesEvent):
""" Monkey patch 'emit_to' from command controller... """
...
@@ -0,0 +1,3 @@
"""
Code Controllers Package
"""
@@ -0,0 +1,33 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Files_Event_Types
from ..command_system import FilesViewCommandSystem
class CommandsController(ControllerBase, list):
def __init__(self):
super(CommandsController, self).__init__()
def _controller_message(self, event: Files_Event_Types.FilesEvent):
if isinstance(event, Files_Event_Types.CreateCommandSystemEvent):
event.response = self.create_command_system()
def create_command_system(self):
command_system = FilesViewCommandSystem()
command_system.emit = self.emit
command_system.emit_to = self.emit_to
self.append(command_system)
return command_system
def remove_command_system(self, event: Files_Event_Types.RemovedFilesViewEvent):
self.remove(event.view.command)
@@ -0,0 +1,27 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Files_Event_Types
class FilesSessionController(ControllerBase, list):
def __init__(self):
super(FilesSessionController, self).__init__()
def _controller_message(self, event: Files_Event_Types.FilesEvent):
if isinstance(event, Files_Event_Types.AddNewFileEvent):
...
def create_tabs_view(self) -> TabsView:
tabs_view = TabsView()
self.add(tabs_view)
self.active_view = self.length
return tabs_view
@@ -0,0 +1,70 @@
# Python imports
# Lib imports
# Application imports
from libs.controllers.controller_base import ControllerBase
from libs.event_factory import Event_Factory, Files_Event_Types
from ..tabs_view import TabsView
from ..tab_dto import TabDTO
class TabViewsController(ControllerBase, list):
def __init__(self):
super(TabViewsController, self).__init__()
self.active_view: int = 0
def _controller_message(self, event: Files_Event_Types.FilesEvent):
if isinstance(event, Files_Event_Types.CreateFilesViewEvent):
...
def _get_command_system(self):
event = Event_Factory.create_event("create_command_system")
self.message_to("commands", event)
command = event.response
del event
return command
def create_tabs_view(self) -> TabsView:
tabs_view = TabsView()
tabs_view.command = self._get_command_system()
self.append(tabs_view)
self.active_view = len(self)
return tabs_view
def set_active_tabs_view(self, i: int):
if i < 0:
i = len(self)
elif i > len(self):
i = 0
self.active_view = i
def get_active_tabs_view(self) -> TabsView:
return self.get(i)
def get_tabs_view(self, i: int = 0) -> TabsView:
if i < 0:
i = len(self)
elif i > len(self):
i = 0
return self.get(i)
def create_new_tab(self, name: str, tabs_view: TabsView = None) -> TabDTO:
if not name:
logger.error("Must give a Tab 'name' to create new tab...")
return
if not tabs_view:
tabs_view = self.get_active_tabs_view()
return tabs_view.create_new_tab(name)
+54
View File
@@ -0,0 +1,54 @@
# Python imports
# Lib imports
# Application imports
from plugins import plugins_controller
from libs.controllers.controller_manager import ControllerManager
from .controllers.files_session_controller import FilesSessionController
from .controllers.commands_controller import CommandsController
from .controllers.tab_views_controller import TabViewsController
class FilesBase:
def __init__(self):
super(FilesBase, self).__init__()
self.controller_manager: ControllerManager = ControllerManager()
self._subscribe_to_events()
self._load_controllers()
def _subscribe_to_events(self):
event_system.subscribe("handle-file", self._load_ipc_file)
event_system.subscribe("handle-files", self._load_ipc_files)
def _load_controllers(self):
files_session_controller = FilesSessionController()
# commands_controller = CommandsController() # Uncomment if not used with 'code' widget
tab_views_controller = TabViewsController()
self.controller_manager.register_controller("files_session", files_session_controller)
# self.controller_manager.register_controller("commands", commands_controller)
self.controller_manager.register_controller("tab_views", tab_views_controller)
# self.controller_manager.register_controller("widgets", widget_registery)
def create_sessioned_tabs_view(self):
return self.controller_manager["files_session"].create_tabs_view()
def create_tabs_view(self):
return self.controller_manager["tab_views"].create_tabs_view()
def _load_ipc_file(self, fpath: str):
active_view = self.controller_manager["tab_views"].get_active_tabs_view()
uris = [ f"file://{fpath}" ]
# active_view._on_uri_data_received(uris)
pass
def _load_ipc_files(self, uris: list):
active_view = self.controller_manager["tab_views"].get_active_tabs_view()
# active_view._on_uri_data_received(uris)
+46
View File
@@ -0,0 +1,46 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class FilesView(Gtk.ScrolledWindow):
def __init__(self):
super(FilesView, self).__init__()
self._setup_styling()
self._setup_signals()
self._subscribe_to_events()
self._load_widgets()
self.show()
def _setup_styling(self):
...
def _setup_signals(self):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
...
def get_icon_grid(self) -> Gtk.IconView:
return self.get_children()[0].get_children()[0]
def bind_icon_grid(self, icon_grid: Gtk.IconView):
self.add(icon_grid)
# viewport = Gtk.Viewport(icon_grid)
# viewport.show()
# self.add(viewport)
+139
View File
@@ -0,0 +1,139 @@
# Python imports
import gc
# 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 GLib
from gi.repository import Gdk
from gi.repository import GdkPixbuf
# Application imports
# from utils.cbindings import gtkmemreaper
class IconGrid(Gtk.IconView):
"""docstring for IconGridWidget"""
def __init__(self):
super(IconGrid, self).__init__()
self._handler_ids = []
self._setup_styling()
self._setup_signals()
self._set_up_dnd()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_pixbuf_column(0)
self.set_text_column(1)
self.set_item_orientation(1)
self.set_selection_mode(3)
self.set_item_width(96)
self.set_item_padding(8)
self.set_margin(12)
self.set_row_spacing(18)
self.set_columns(-1)
self.set_spacing(12)
self.set_column_spacing(18)
def _setup_signals(self):
...
def _setup_additional_signals(self,
grid_icon_single_click,
grid_icon_double_click,
grid_set_selected_items,
grid_on_drag_set,
grid_on_drag_data_received,
grid_on_drag_motion
):
self._handler_ids = [
self.connect("button-release-event", grid_icon_single_click),
self.connect("item-activated", grid_icon_double_click),
self.connect("selection-changed", grid_set_selected_items),
self.connect("drag-data-get", grid_on_drag_set),
self.connect("drag-data-received", grid_on_drag_data_received),
self.connect("drag-motion", grid_on_drag_motion)
]
def _destroy(grid):
self.clear_signals_and_data()
self._handler_ids.append(self.connect("destroy", _destroy))
def _load_widgets(self):
self.clear_and_set_new_store()
def _set_up_dnd(self):
URI_TARGET_TYPE = 80
uri_target = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE)
targets = [ uri_target ]
action = Gdk.DragAction.COPY
self.enable_model_drag_dest(targets, action)
self.enable_model_drag_source(0, targets, action)
def get_store(self):
return self.get_model()
def clear_and_set_new_store(self):
self._clear_store()
self.set_model(
Gtk.ListStore(
GdkPixbuf.Pixbuf or GdkPixbuf.PixbufAnimation or None, str or None
)
)
def clear_signals_and_data(self):
self.unset_model_drag_dest()
self.unset_model_drag_source()
for handle_id in self._handler_ids:
self.disconnect(handle_id)
self._handler_ids.clear()
self._clear_store()
def _clear_store(self):
store = self.get_model()
self.set_model(None)
if not store: return
iter = store.get_iter_first()
while iter:
icon = store.get_value(iter, 0)
store.set_value(iter, 0, None)
store.set_value(iter, 1, None)
iter = store.iter_next(iter)
if icon:
logger.debug(f"Reference count for icon is: {icon.__grefcount__}")
icon.run_dispose()
# icon_ptr = int(hash(icon)) # WARNING: not stable across runs
# gtkmemreaper.free_pixbuf(icon_ptr)
del icon
store.clear()
store.run_dispose()
# GLib.idle_add(store.clear)
# GLib.idle_add(store.run_dispose)
# store.run_dispose()
# store_ptr = int(hash(store)) # WARNING: not stable across runs
# gtkmemreaper.free_list_store(store)
del store
gc.collect()
+20
View File
@@ -0,0 +1,20 @@
# Python imports
# Lib imports
# Application imports
from .tab_header import TabHeader
from .files_view import FilesView
from .icon_grid import IconGrid
class TabDTO:
def __init__(self):
super(TabDTO, self).__init__()
self.tab_header: TabHeader = TabHeader()
self.files_view: FilesView = FilesView()
self.icon_grid: IconGrid = IconGrid()
self.files_view.bind_icon_grid(self.icon_grid)
+58
View File
@@ -0,0 +1,58 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
class TabHeader(Gtk.Box):
"""docstring for TabHeader"""
def __init__(self):
super(TabHeader, self).__init__()
self._setup_styling()
self._setup_signals()
self._load_widgets()
self.show_all()
def _setup_styling(self):
self.set_orientation(0)
self.set_hexpand(False)
def _setup_signals(self):
...
def _load_widgets(self):
self.label = Gtk.Label()
self.close_bttn = Gtk.Button()
icon = Gtk.Image(stock = Gtk.STOCK_CLOSE)
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.add(self.label)
self.add(self.close_bttn)
def bind_close_signal(self, close_method: callable):
self._close_handler_id = self.close_bttn.connect("released", close_method)
def clear_signals_and_data(self):
self.close_bttn.disconnect(self._close_handler_id)
self._close_tab = None
self._close_handler_id = None
for child in self.get_children():
child.unparent()
child.run_dispose()
child.destroy()
+54
View File
@@ -0,0 +1,54 @@
# Python imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
# Application imports
from .tab_dto import TabDTO
class TabsView(Gtk.Notebook):
def __init__(self):
super(TabsView, 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):
...
def _subscribe_to_events(self):
...
def _load_widgets(self):
...
def create_new_tab(self, name: str) -> TabDTO:
if not name:
logger.error("Must give a path to load...")
return
tab_dto = TabDTO()
tab_dto.tab_header.label.set_label(name)
self.append_page(
tab_dto.files_view,
tab_dto.tab_header
)
self.set_tab_detachable(tab_dto.files_view, True)
self.set_tab_reorderable(tab_dto.files_view, True)
return tab_dto
+15
View File
@@ -0,0 +1,15 @@
"""
Libs Code DTO(s) Events Package
"""
from .files_event import FilesEvent
from .create_files_view_event import CreateFilesViewEvent
from .created_files_view_event import CreatedFilesViewEvent
from .remove_files_view_event import RemoveFilesViewEvent
from .create_command_system_event import CreateCommandSystemEvent
from .added_new_file_event import AddedNewFileEvent
from .add_new_file_event import AddNewFileEvent
@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
from .files_event import FilesEvent
@dataclass
class AddNewFileEvent(FilesEvent):
...
@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
from .files_event import FilesEvent
@dataclass
class AddedNewFileEvent(FilesEvent):
...
@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
from .files_event import FilesEvent
@dataclass
class CreateCommandSystemEvent(FilesEvent):
...
@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass
# Lib imports
# Application imports
from .files_event import FilesEvent
@dataclass
class CreateFilesViewEvent(FilesEvent):
...
@@ -0,0 +1,13 @@
# Python imports
from dataclasses import dataclass
# Lib imports
# Application imports
from .files_event import FilesEvent
@dataclass
class CreatedFilesViewEvent(FilesEvent):
...
+15
View File
@@ -0,0 +1,15 @@
# Python imports
from dataclasses import dataclass, field
# Lib imports
# Application imports
from ...base_event import BaseEvent
@dataclass
class FilesEvent(BaseEvent):
view: any = None
tab: any = None
response: any = None
@@ -0,0 +1,14 @@
# Python imports
from dataclasses import dataclass
# Lib imports
# Application imports
from .files_event import FilesEvent
from libs.dto.states.source_view_states import SourceViewStates
@dataclass
class RemoveFilesViewEvent(FilesEvent):
...
+6 -3
View File
@@ -10,6 +10,7 @@ from .singleton import Singleton
from .dto.base_event import BaseEvent
from .dto.code import events as code
from .dto.files import events as files
@@ -18,6 +19,7 @@ class EventFactory(Singleton):
self._event_classes: Dict[str, Type[BaseEvent]] = {}
self._auto_register_events( files.__dict__.items() )
self._auto_register_events( code.__dict__.items() )
def register_event(self, event_type: str, event_class: Type[BaseEvent]):
@@ -98,7 +100,8 @@ class EventNamespace:
App_Event_Types = EventNamespace()
Code_Event_Types = App_Event_Types
Event_Factory = EventFactory()
App_Event_Types = EventNamespace()
Code_Event_Types = App_Event_Types
Files_Event_Types = App_Event_Types
Event_Factory = EventFactory()