diff --git a/src/core/containers/editors_container.py b/src/core/containers/editors_container.py index e24554e..93e1b10 100644 --- a/src/core/containers/editors_container.py +++ b/src/core/containers/editors_container.py @@ -9,6 +9,7 @@ from gi.repository import Gtk from ..widgets.separator_widget import Separator from ..widgets.miniview_widget import MiniViewWidget from ..widgets.base.notebook.editor_notebook import EditorNotebook +from .fixed_box import FixedBox @@ -34,8 +35,11 @@ class EditorsPaned(Gtk.Paned): event_system.subscribe("update_paned_handle", self._update_paned_handle) def _load_widgets(self): - self.add1(EditorNotebook()) - self.add2(EditorNotebook()) + self.add1(FixedBox()) + self.add2(FixedBox()) + + # self.add1(EditorNotebook()) + # self.add2(EditorNotebook()) def _update_paned_handle(self): rect = self.get_allocation() diff --git a/src/core/containers/fixed_box.py b/src/core/containers/fixed_box.py new file mode 100644 index 0000000..c041307 --- /dev/null +++ b/src/core/containers/fixed_box.py @@ -0,0 +1,52 @@ +# Python imports + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports +from ..widgets.base.webkit.dnd_box import DnDBox +from ..widgets.base.webkit.ace_editor import AceEditor + + + +class FixedBox(Gtk.Fixed): + """ + In order for the desired Drag and Drop we need to stack a widget + (aka our DnDBox) above the Webkit2.Webview to intercept and proxy accordingly. + """ + + def __init__(self): + super(FixedBox, self).__init__() + + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_widgets() + + self.show_all() + + + def _setup_styling(self): + self.size_allocate( self.get_parent().get_allocated_size().allocation ) + + def _setup_signals(self): + self.connect("realize", self._on_realize) + self.connect("size-allocate", self._on_size_allocate) + + def _subscribe_to_events(self): + ... + + def _load_widgets(self): + self.ace_editor = AceEditor() + self.dnd_box = DnDBox() + + self.add( self.ace_editor ) + self.add( self.dnd_box ) + + def _on_realize(self, wiodget): + self._setup_styling() + + def _on_size_allocate(self, widget, allocation): + self.ace_editor.size_allocate( allocation ) \ No newline at end of file diff --git a/src/core/controllers/base_controller.py b/src/core/controllers/base_controller.py index cdcf13e..9e68efe 100644 --- a/src/core/controllers/base_controller.py +++ b/src/core/controllers/base_controller.py @@ -13,6 +13,8 @@ from gi.repository import GLib from ..mixins.signals_mixins import SignalsMixins from ..containers.core_widget import CoreWidget from .base_controller_data import BaseControllerData +from .bridge_controller import BridgeController +from .files_controller import FilesController @@ -32,6 +34,7 @@ class BaseController(SignalsMixins, BaseControllerData): self._setup_styling() self._setup_signals() self._subscribe_to_events() + self._load_controllers() if args.no_plugins == "false": self.plugins.launch_plugins() @@ -54,6 +57,10 @@ class BaseController(SignalsMixins, BaseControllerData): event_system.subscribe("set_active_src_view", self.set_active_src_view) event_system.subscribe("get_active_src_view", self.get_active_src_view) + def _load_controllers(self): + BridgeController() + FilesController() + def load_glade_file(self): self.builder = Gtk.Builder() self.builder.add_from_file(settings_manager.get_glade_file()) diff --git a/src/core/controllers/files_controller.py b/src/core/controllers/files_controller.py index 9a626c0..d0a161e 100644 --- a/src/core/controllers/files_controller.py +++ b/src/core/controllers/files_controller.py @@ -93,5 +93,4 @@ class FilesController: event_system.emit("ui_message", (message, "error",)) return False - return True - + return True \ No newline at end of file diff --git a/src/core/widgets/base/webkit/__init__.py b/src/core/widgets/base/webkit/__init__.py new file mode 100644 index 0000000..ba43057 --- /dev/null +++ b/src/core/widgets/base/webkit/__init__.py @@ -0,0 +1,3 @@ +""" + Widgets.Base.Webkit Module +""" \ No newline at end of file diff --git a/src/core/widgets/base/webkit/ace_editor.py b/src/core/widgets/base/webkit/ace_editor.py new file mode 100644 index 0000000..e331f9d --- /dev/null +++ b/src/core/widgets/base/webkit/ace_editor.py @@ -0,0 +1,120 @@ +# Python imports +import json + +# Lib imports +import gi +gi.require_version('Gdk', '3.0') +gi.require_version('WebKit2', '4.0') +from gi.repository import Gdk +from gi.repository import WebKit2 +from gi.repository import GLib + +# Application imports +from libs.data_types import Event + + + +class AceEditor(WebKit2.WebView): + def __init__(self): + super(AceEditor, self).__init__() + + # self.get_context().set_sandbox_enabled(False) + + self._load_settings() + self._setup_styling() + self._setup_signals() + self._subscribe_to_events() + self._load_view() + self._setup_content_manager() + + self.show_all() + + if settings_manager.is_debug(): + inspector = self.get_inspector() + inspector.show() + + def _setup_styling(self): + self.set_vexpand(True) + self.set_hexpand(True) + self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) ) + + def _setup_signals(self): + ... + + def _subscribe_to_events(self): + event_system.subscribe("load_file", self.load_file) + + event_system.subscribe("updated_tab", self.updated_tab) + event_system.subscribe("ui_message", self.ui_message) + + + def _load_settings(self): + self.set_settings( WebkitUISettings() ) + + def _load_view(self): + path = settings_manager.get_context_path() + data = None + + with open(f"{path}/index.html", "r") as f: + data = f.read() + + self.load_html(content = data, base_uri = f"file://{path}/") + + def _setup_content_manager(self): + content_manager = self.get_user_content_manager() + + content_manager.connect("script-message-received", self._process_js_message) + content_manager.register_script_message_handler("backend") + + def _process_js_message(self, user_content_manager, js_result): + js_value = js_result.get_js_value() + message = js_value.to_string() + + try: + event = Event( **json.loads(message) ) + event_system.emit("handle_bridge_event", (event,)) + except Exception as e: + logger.info(e) + + def load_file(self, ftype: str, fhash: str, file: str, content: str): + command = f"loadFile('{ftype}', '{fhash}', '{file}', '{content}')" + self.run_javascript(command, None, None) + + def updated_tab(self, ftype, fname): + command = f"updatedTab('{ftype}', '{fname}')" + self.run_javascript(command, None, None) + + def ui_message(self, message, mtype): + command = f"displayMessage('{message}', '{mtype}', '3')" + self.run_javascript(command, None, None) + + +class WebkitUISettings(WebKit2.Settings): + def __init__(self): + super(WebkitUISettings, self).__init__() + + self._set_default_settings() + + + # Note: Highly insecure setup but most "app" like setup I could think of. + # Audit heavily any scripts/links ran/clicked under this setup! + def _set_default_settings(self): + self.set_enable_xss_auditor(False) + self.set_enable_hyperlink_auditing(False) + self.set_allow_file_access_from_file_urls(True) + self.set_allow_universal_access_from_file_urls(True) + + self.set_enable_page_cache(False) + self.set_enable_offline_web_application_cache(False) + self.set_enable_html5_local_storage(False) + self.set_enable_html5_database(False) + + self.set_enable_fullscreen(False) + self.set_print_backgrounds(False) + self.set_enable_tabs_to_links(False) + self.set_enable_developer_extras(True) + self.set_enable_webrtc(True) + self.set_enable_webaudio(True) + self.set_enable_accelerated_2d_canvas(True) + + self.set_user_agent(f"{app_name}") \ No newline at end of file diff --git a/src/core/widgets/base/webkit/dnd_box.py b/src/core/widgets/base/webkit/dnd_box.py new file mode 100644 index 0000000..91dad8f --- /dev/null +++ b/src/core/widgets/base/webkit/dnd_box.py @@ -0,0 +1,56 @@ +# 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.mixins.dnd_mixin import DnDMixin + + + +class DnDBox(DnDMixin, Gtk.DrawingArea): + """ + DnDBox is used as a suplamentary way to Drag and Drop files because + Webkit2.WebView is causing issues with signal intercepts and I don't + understand how to acount for non DnD events. + """ + + def __init__(self): + super(DnDBox, 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): + self.set_events(Gdk.EventMask.ALL_EVENTS_MASK) + self._setup_dnd() + + def _subscribe_to_events(self): + event_system.subscribe("listen_dnd_signals", self._listen_dnd_signals_pre) + event_system.subscribe("pause_dnd_signals", self._pause_dnd_signals_pre) + + def _load_widgets(self): + ... + + def _listen_dnd_signals_pre(self): + allocation = self.get_parent().get_allocated_size().allocation + self.size_allocate( allocation ) + self.grab_focus() + + def _pause_dnd_signals_pre(self): + allocation = self.get_allocated_size().allocation + allocation.widrh = 0 + allocation.height = 0 + self.size_allocate( allocation ) \ No newline at end of file diff --git a/src/core/window.py b/src/core/window.py index 96a9193..b3a54fd 100644 --- a/src/core/window.py +++ b/src/core/window.py @@ -51,6 +51,9 @@ class Window(Gtk.ApplicationWindow): ctx.add_class(f"mw_transparency_{settings.theming.transparency}") def _setup_signals(self): + self.connect("focus-in-event", self._on_focus_in_event) + self.connect("focus-out-event", self._on_focus_out_event) + self.connect("delete-event", self._tear_down) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self._tear_down) @@ -102,10 +105,16 @@ class Window(Gtk.ApplicationWindow): cr.paint() cr.set_operator(cairo.OPERATOR_OVER) + + def _on_focus_in_event(self, widget, event): + event_system.emit("pause_dnd_signals") + + def _on_focus_out_event(self, widget, event): + event_system.emit("listen_dnd_signals") + def _load_interactive_debug(self): self.set_interactive_debugging(True) - def _tear_down(self, widget = None, eve = None): event_system.emit("shutting_down") diff --git a/src/libs/mixins/dnd_mixin.py b/src/libs/mixins/dnd_mixin.py index e66f7cc..4825a99 100644 --- a/src/libs/mixins/dnd_mixin.py +++ b/src/libs/mixins/dnd_mixin.py @@ -68,3 +68,4 @@ class DnDMixin: files.append(gfile) event_system.emit('set_pre_drop_dnd', (files,)) + diff --git a/src/libs/settings_manager/manager.py b/src/libs/settings_manager/manager.py index b37b78c..738427f 100644 --- a/src/libs/settings_manager/manager.py +++ b/src/libs/settings_manager/manager.py @@ -26,6 +26,7 @@ class SettingsManager(StartCheckMixin, Singleton): self._USR_PATH = f"/usr/share/{app_name.lower()}" self._USR_CONFIG_FILE = f"{self._USR_PATH}/settings.json" + self._CONTEXT_PATH = f"{self._HOME_CONFIG_PATH}/context_path" self._PLUGINS_PATH = f"{self._HOME_CONFIG_PATH}/plugins" self._DEFAULT_ICONS = f"{self._HOME_CONFIG_PATH}/icons" self._CONFIG_FILE = f"{self._HOME_CONFIG_PATH}/settings.json" @@ -126,6 +127,7 @@ class SettingsManager(StartCheckMixin, Singleton): def get_ui_widgets_path(self) -> str: return self._UI_WIDEGTS_PATH def get_context_menu_data(self) -> str: return self._context_menu_data + def get_context_path(self) -> str: return self._CONTEXT_PATH def get_plugins_path(self) -> str: return self._PLUGINS_PATH def get_icon_theme(self) -> str: return self._ICON_THEME def get_css_file(self) -> str: return self._CSS_FILE @@ -171,4 +173,4 @@ class SettingsManager(StartCheckMixin, Singleton): def save_settings(self): with open(self._CONFIG_FILE, 'w') as outfile: - json.dump(self.settings.as_dict(), outfile, separators=(',', ':'), indent=4) + json.dump(self.settings.as_dict(), outfile, separators=(',', ':'), indent=4) \ No newline at end of file