Add file deletion detection, WebKit improvements, and bug fixes
- Add FileExternallyDeletedEvent with UI indicator in tabs for deleted files - Set minimum height (300px) for editors container - Add Developer Tools to WebKit context menu - Fix DnD URI handling when uris list is empty - Fix buffer replacement using freeze_notify and proper bounds handling - Move webkit_ui_settings to new path - Remove <change_me> placeholders from APP_NAME and user config
This commit is contained in:
@@ -43,7 +43,7 @@ def call_chain_wrapper(fn):
|
|||||||
|
|
||||||
# NOTE: Just reminding myself we can add to builtins two different ways...
|
# NOTE: Just reminding myself we can add to builtins two different ways...
|
||||||
# __builtins__.update({"event_system": Builtins()})
|
# __builtins__.update({"event_system": Builtins()})
|
||||||
builtins.APP_NAME = "<change_me>"
|
builtins.APP_NAME = "<change_me>".replace("<","").replace(">","")
|
||||||
|
|
||||||
builtins.keybindings = Keybindings()
|
builtins.keybindings = Keybindings()
|
||||||
builtins.event_system = EventSystem()
|
builtins.event_system = EventSystem()
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class EditorsContainer(Gtk.Paned):
|
|||||||
self.ctx = self.get_style_context()
|
self.ctx = self.get_style_context()
|
||||||
self.ctx.add_class("paned-editors-container")
|
self.ctx.add_class("paned-editors-container")
|
||||||
|
|
||||||
|
self.set_size_request(-1, 300)
|
||||||
self.set_hexpand(True)
|
self.set_hexpand(True)
|
||||||
self.set_vexpand(True)
|
self.set_vexpand(True)
|
||||||
self.set_wide_handle(True)
|
self.set_wide_handle(True)
|
||||||
|
|||||||
@@ -27,4 +27,5 @@ def execute(
|
|||||||
)
|
)
|
||||||
|
|
||||||
view.set_buffer(file.buffer)
|
view.set_buffer(file.buffer)
|
||||||
|
|
||||||
update_info_bar_if_focused(view.command, view)
|
update_info_bar_if_focused(view.command, view)
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class TabsController(ControllerBase):
|
|||||||
self.update_tab_label(event)
|
self.update_tab_label(event)
|
||||||
elif isinstance(event, Code_Event_Types.ModifiedChangedEvent):
|
elif isinstance(event, Code_Event_Types.ModifiedChangedEvent):
|
||||||
self.tabs_widget.modified_changed( event.buffer )
|
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):
|
elif isinstance(event, Code_Event_Types.AddedNewFileEvent):
|
||||||
self.add_tab(event)
|
self.add_tab(event)
|
||||||
elif isinstance(event, Code_Event_Types.PoppedFileEvent):
|
elif isinstance(event, Code_Event_Types.PoppedFileEvent):
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ class SourceViewDnDMixin:
|
|||||||
if info == 80:
|
if info == 80:
|
||||||
uris = data.get_uris()
|
uris = data.get_uris()
|
||||||
|
|
||||||
if not uris: return
|
if not uris:
|
||||||
uris = data.get_text().split("\n")
|
uris = data.get_text().split("\n")
|
||||||
|
|
||||||
self._on_uri_data_received(uris)
|
self._on_uri_data_received(uris)
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class SourceFile(GtkSource.File):
|
|||||||
self.fname: str = "buffer"
|
self.fname: str = "buffer"
|
||||||
self.fpath: str = "buffer"
|
self.fpath: str = "buffer"
|
||||||
self.ftype: str = "buffer"
|
self.ftype: str = "buffer"
|
||||||
|
self.was_deleted: bool = False
|
||||||
self.buffer: SourceBuffer = SourceBuffer()
|
self.buffer: SourceBuffer = SourceBuffer()
|
||||||
|
|
||||||
self._set_signals()
|
self._set_signals()
|
||||||
@@ -56,17 +57,22 @@ class SourceFile(GtkSource.File):
|
|||||||
self.emit(event)
|
self.emit(event)
|
||||||
|
|
||||||
if self.is_deleted():
|
if self.is_deleted():
|
||||||
print("is_deleted")
|
self.was_deleted = True
|
||||||
# event = Event_Factory.create_event("file_deleted", buffer = buffer)
|
event = Event_Factory.create_event(
|
||||||
# event.file = self
|
"file_externally_deleted",
|
||||||
# self.emit(event)
|
file = self,
|
||||||
|
buffer = buffer
|
||||||
|
)
|
||||||
|
self.emit(event)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.is_externally_modified():
|
if self.is_externally_modified():
|
||||||
print("is_externally_modified")
|
# event = Event_Factory.create_event(
|
||||||
# event = Event_Factory.create_event("file_externally_modified", buffer = buffer)
|
# "file_externally_modified",
|
||||||
# event.file = self
|
# file = self,
|
||||||
# self.emit(event)
|
# buffer = buffer
|
||||||
|
# )
|
||||||
|
# self.emit(event)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _insert_text(
|
def _insert_text(
|
||||||
@@ -128,6 +134,12 @@ class SourceFile(GtkSource.File):
|
|||||||
|
|
||||||
f.write(text)
|
f.write(text)
|
||||||
|
|
||||||
|
if self.was_deleted:
|
||||||
|
self.was_deleted = False
|
||||||
|
# self.set_path(gfile)
|
||||||
|
self.set_location( None )
|
||||||
|
self.set_location( gfile )
|
||||||
|
|
||||||
return gfile
|
return gfile
|
||||||
|
|
||||||
|
|
||||||
@@ -135,11 +147,22 @@ class SourceFile(GtkSource.File):
|
|||||||
if not gfile: return
|
if not gfile: return
|
||||||
|
|
||||||
self.set_path(gfile)
|
self.set_path(gfile)
|
||||||
data = gfile.load_bytes()[0].get_data().decode("UTF-8")
|
text = gfile.load_bytes()[0].get_data().decode("UTF-8")
|
||||||
undo_manager = self.buffer.get_undo_manager()
|
undo_manager = self.buffer.get_undo_manager()
|
||||||
|
|
||||||
|
def move_insert_to_start():
|
||||||
|
start_itr = self.buffer.get_start_iter()
|
||||||
|
self.buffer.place_cursor(start_itr)
|
||||||
|
|
||||||
undo_manager.begin_not_undoable_action()
|
undo_manager.begin_not_undoable_action()
|
||||||
self.buffer.insert_at_cursor(data)
|
|
||||||
|
with self.buffer.freeze_notify():
|
||||||
|
start_itr, end_itr = self.buffer.get_bounds()
|
||||||
|
|
||||||
|
self.buffer.delete(start_itr, end_itr)
|
||||||
|
self.buffer.insert(start_itr, text, -1)
|
||||||
|
GLib.idle_add(move_insert_to_start)
|
||||||
|
|
||||||
undo_manager.end_not_undoable_action()
|
undo_manager.end_not_undoable_action()
|
||||||
self.buffer.set_modified(False)
|
self.buffer.set_modified(False)
|
||||||
|
|
||||||
|
|||||||
@@ -122,11 +122,19 @@ class TabsWidget(Gtk.Notebook):
|
|||||||
if not buffer == tab.file.buffer: continue
|
if not buffer == tab.file.buffer: continue
|
||||||
|
|
||||||
ctx = tab.label.get_style_context()
|
ctx = tab.label.get_style_context()
|
||||||
|
ctx.remove_class("file-deleted")
|
||||||
if buffer.get_modified():
|
if buffer.get_modified():
|
||||||
ctx.add_class("file-changed")
|
ctx.add_class("file-changed")
|
||||||
else:
|
else:
|
||||||
ctx.remove_class("file-changed")
|
ctx.remove_class("file-changed")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
def close_item(self, menu_item, page_widget):
|
def close_item(self, menu_item, page_widget):
|
||||||
tab = self.get_tab_label(page_widget)
|
tab = self.get_tab_label(page_widget)
|
||||||
tab.close_bttn.clicked()
|
tab.close_bttn.clicked()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# Python imports
|
# Python imports
|
||||||
|
from pathlib import Path
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Lib imports
|
# Lib imports
|
||||||
@@ -6,10 +7,12 @@ import gi
|
|||||||
gi.require_version('Gdk', '3.0')
|
gi.require_version('Gdk', '3.0')
|
||||||
gi.require_version('WebKit2', '4.0')
|
gi.require_version('WebKit2', '4.0')
|
||||||
from gi.repository import Gdk
|
from gi.repository import Gdk
|
||||||
|
from gi.repository import Gtk
|
||||||
|
from gi.repository import Gio
|
||||||
from gi.repository import WebKit2
|
from gi.repository import WebKit2
|
||||||
|
|
||||||
# Application imports
|
# Application imports
|
||||||
from libs.settings.other.webkit_ui_settings import WebkitUISettings
|
from libs.settings.webkit.webkit_ui_settings import WebkitUISettings
|
||||||
from libs.dto.base_event import BaseEvent
|
from libs.dto.base_event import BaseEvent
|
||||||
|
|
||||||
|
|
||||||
@@ -17,7 +20,9 @@ class WebkitUI(WebKit2.WebView):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(WebkitUI, self).__init__()
|
super(WebkitUI, self).__init__()
|
||||||
|
|
||||||
|
self._load_settings()
|
||||||
self._setup_styling()
|
self._setup_styling()
|
||||||
|
self._setup_signals()
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
self._setup_content_manager()
|
self._setup_content_manager()
|
||||||
|
|
||||||
@@ -29,6 +34,9 @@ class WebkitUI(WebKit2.WebView):
|
|||||||
self.set_hexpand(True)
|
self.set_hexpand(True)
|
||||||
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
|
self.set_background_color( Gdk.RGBA(0, 0, 0, 0.0) )
|
||||||
|
|
||||||
|
def _setup_signals(self):
|
||||||
|
self.connect("context-menu", self._on_context_menu)
|
||||||
|
|
||||||
def _subscribe_to_events(self):
|
def _subscribe_to_events(self):
|
||||||
event_system.subscribe(f"ui-message", self.ui_message)
|
event_system.subscribe(f"ui-message", self.ui_message)
|
||||||
|
|
||||||
@@ -50,6 +58,18 @@ class WebkitUI(WebKit2.WebView):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.info(e)
|
logger.info(e)
|
||||||
|
|
||||||
|
def _on_context_menu(self, web_view, context_menu, event, hit_test_result):
|
||||||
|
action = Gio.SimpleAction.new("Developer Tools", None)
|
||||||
|
item = WebKit2.ContextMenuItem.new_from_gaction(action, "Developer Tools")
|
||||||
|
|
||||||
|
def show_developer_tools(action, parameter):
|
||||||
|
inspector = self.get_inspector()
|
||||||
|
inspector.show()
|
||||||
|
|
||||||
|
action.connect("activate", show_developer_tools)
|
||||||
|
|
||||||
|
context_menu.append(item)
|
||||||
|
|
||||||
def load_url(self, url: str = ""):
|
def load_url(self, url: str = ""):
|
||||||
if not url:
|
if not url:
|
||||||
url = "https://duckduckgo.com/"
|
url = "https://duckduckgo.com/"
|
||||||
@@ -58,13 +78,23 @@ class WebkitUI(WebKit2.WebView):
|
|||||||
|
|
||||||
def load_context_base_path(self, path: str = ""):
|
def load_context_base_path(self, path: str = ""):
|
||||||
if not path:
|
if not path:
|
||||||
path = settings_manager.path_manager.get_context_path()
|
path = settings_manager.path_manager.get_context_path()
|
||||||
|
|
||||||
data = None
|
base_path = Path(path)
|
||||||
with open(f"{path}/index.html", "r") as f:
|
index_file = base_path / "index.html"
|
||||||
data = f.read()
|
|
||||||
|
|
||||||
self.load_html(content = data, base_uri = f"file://{path}")
|
if not index_file.exists():
|
||||||
|
raise FileNotFoundError(f"index.html not found in {base_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = index_file.read_text(encoding = "utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Failed to read {index_file}: {e}")
|
||||||
|
|
||||||
|
self.load_html(
|
||||||
|
content = data,
|
||||||
|
base_uri = index_file.as_uri()
|
||||||
|
)
|
||||||
|
|
||||||
def ui_message(self, message, mtype):
|
def ui_message(self, message, mtype):
|
||||||
command = f"displayMessage('{message}', '{mtype}', '3')"
|
command = f"displayMessage('{message}', '{mtype}', '3')"
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
from .code_event import CodeEvent
|
from .code_event import CodeEvent
|
||||||
from .register_provider_event import RegisterProviderEvent
|
from .register_provider_event import RegisterProviderEvent
|
||||||
from .register_command_event import RegisterCommandEvent
|
from .register_command_event import RegisterCommandEvent
|
||||||
|
from .file_externally_modified_event import FileExternallyModifiedEvent
|
||||||
|
from .file_externally_deleted_event import FileExternallyDeletedEvent
|
||||||
|
|
||||||
from .get_new_command_system_event import GetNewCommandSystemEvent
|
from .get_new_command_system_event import GetNewCommandSystemEvent
|
||||||
from .request_completion_event import RequestCompletionEvent
|
from .request_completion_event import RequestCompletionEvent
|
||||||
|
|||||||
13
src/libs/dto/code/file_externally_deleted_event.py
Normal file
13
src/libs/dto/code/file_externally_deleted_event.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Python imports
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from .code_event import CodeEvent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FileExternallyDeletedEvent(CodeEvent):
|
||||||
|
...
|
||||||
13
src/libs/dto/code/file_externally_modified_event.py
Normal file
13
src/libs/dto/code/file_externally_modified_event.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Python imports
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
# Lib imports
|
||||||
|
|
||||||
|
# Application imports
|
||||||
|
from .code_event import CodeEvent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FileExternallyModifiedEvent(CodeEvent):
|
||||||
|
...
|
||||||
@@ -95,7 +95,7 @@ class WebkitUISettings(WebKit2.Settings):
|
|||||||
self.set_property('javascript-can-open-windows-automatically', False)
|
self.set_property('javascript-can-open-windows-automatically', False)
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
self.set_property('enable-developer-extras', False)
|
self.set_property('enable-developer-extras', True)
|
||||||
self.set_property('enable-write-console-messages-to-stdout', False)
|
self.set_property('enable-write-console-messages-to-stdout', False)
|
||||||
self.set_property('draw-compositing-indicators', False)
|
self.set_property('draw-compositing-indicators', False)
|
||||||
self.set_property('enable-mock-capture-devices', False)
|
self.set_property('enable-mock-capture-devices', False)
|
||||||
@@ -3,14 +3,17 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Gtk + HTML + Python App</title>
|
<title>Gtk + HTML + Python App</title>
|
||||||
<!-- Bootstrap CSS -->
|
<!-- <base href="/"> -->
|
||||||
<link rel="stylesheet" href="resources/css/libs/bootstrap5/bootstrap.min.css">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="resources/css/libs/bootstrap-icons/bootstrap-icons.css">
|
|
||||||
|
|
||||||
<!-- Site CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<!-- <link rel="stylesheet" href="resources/css/context-menu.css"> -->
|
<link rel="stylesheet" href="resources/css/libs/bootstrap5/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="resources/css/main.css">
|
<link rel="stylesheet" href="resources/css/libs/bootstrap-icons/bootstrap-icons.css">
|
||||||
<link rel="stylesheet" href="resources/css/overrides.css">
|
|
||||||
|
<!-- Site CSS -->
|
||||||
|
<!-- <link rel="stylesheet" href="resources/css/context-menu.css"> -->
|
||||||
|
<link rel="stylesheet" href="resources/css/main.css">
|
||||||
|
<link rel="stylesheet" href="resources/css/overrides.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Reference in New Issue
Block a user