Add debounced text change handling and modified file indicator for tabs
- Debounce word completer refresh with 1500ms timeout to reduce overhead - Add visual indicator (file-changed class) for modified files in tabs - Refactor buffer switching into signal_mapper for DRY code - Fix handler ID indices after adding _after_changed signal - Move set_modified(False) after successful file write in save()
This commit is contained in:
@@ -20,6 +20,7 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
|||||||
super(ProviderResponseCache, self).__init__()
|
super(ProviderResponseCache, self).__init__()
|
||||||
|
|
||||||
self.matchers: dict = {}
|
self.matchers: dict = {}
|
||||||
|
self._temp_timeout_id: int = None
|
||||||
|
|
||||||
|
|
||||||
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
def process_file_load(self, event: Code_Event_Types.AddedNewFileEvent):
|
||||||
@@ -36,9 +37,22 @@ class ProviderResponseCache(ProviderResponseCacheBase):
|
|||||||
|
|
||||||
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
def process_file_change(self, event: Code_Event_Types.TextChangedEvent):
|
||||||
buffer = event.file.buffer
|
buffer = event.file.buffer
|
||||||
|
self._clear_temp_delay()
|
||||||
|
self._set_temp_delay(buffer)
|
||||||
|
|
||||||
|
def _clear_temp_delay(self):
|
||||||
|
if self._temp_timeout_id:
|
||||||
|
GLib.source_remove(self._temp_timeout_id)
|
||||||
|
|
||||||
|
def _set_temp_delay(self, buffer):
|
||||||
|
def run_refresh_update(buffer):
|
||||||
with ThreadPoolExecutor(max_workers = 1) as executor:
|
with ThreadPoolExecutor(max_workers = 1) as executor:
|
||||||
executor.submit(self._handle_change, buffer)
|
executor.submit(self._handle_change, buffer)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._temp_timeout_id = GLib.timeout_add(1500, run_refresh_update, buffer)
|
||||||
|
|
||||||
def _handle_change(self, buffer):
|
def _handle_change(self, buffer):
|
||||||
start_itr = buffer.get_start_iter()
|
start_itr = buffer.get_start_iter()
|
||||||
end_itr = buffer.get_end_iter()
|
end_itr = buffer.get_end_iter()
|
||||||
|
|||||||
@@ -26,11 +26,11 @@ class TabsController(ControllerBase):
|
|||||||
|
|
||||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||||
if isinstance(event, Code_Event_Types.FocusedViewEvent):
|
if isinstance(event, Code_Event_Types.FocusedViewEvent):
|
||||||
self.tabs_widget.view_changed(
|
self.tabs_widget.view_changed( event.view.get_buffer() )
|
||||||
event.view.get_buffer()
|
|
||||||
)
|
|
||||||
elif isinstance(event, Code_Event_Types.FilePathSetEvent):
|
elif isinstance(event, Code_Event_Types.FilePathSetEvent):
|
||||||
self.update_tab_label(event)
|
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.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):
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class SourceViewSignalMapper:
|
|||||||
def set_state_manager(self, state_manager):
|
def set_state_manager(self, state_manager):
|
||||||
self.state_manager = state_manager
|
self.state_manager = state_manager
|
||||||
|
|
||||||
|
def set_buffer_to_active_view(self, buffer):
|
||||||
|
self.active_view.set_buffer(buffer)
|
||||||
|
self.active_view.command.exec("update_info_bar")
|
||||||
|
|
||||||
def connect_signals(self, source_view: SourceView):
|
def connect_signals(self, source_view: SourceView):
|
||||||
signal_mappings = self._get_signal_mappings()
|
signal_mappings = self._get_signal_mappings()
|
||||||
for signal, handler in signal_mappings.items():
|
for signal, handler in signal_mappings.items():
|
||||||
|
|||||||
@@ -28,17 +28,14 @@ class SourceViewsController(ControllerBase, list):
|
|||||||
if isinstance(event, Code_Event_Types.RemovedFileEvent):
|
if isinstance(event, Code_Event_Types.RemovedFileEvent):
|
||||||
self._remove_file(event)
|
self._remove_file(event)
|
||||||
elif isinstance(event, Code_Event_Types.RegisterCommandEvent):
|
elif isinstance(event, Code_Event_Types.RegisterCommandEvent):
|
||||||
self. _register_command(event)
|
self._register_command(event)
|
||||||
|
|
||||||
if not self.signal_mapper.active_view: return
|
if not self.signal_mapper.active_view: return
|
||||||
|
|
||||||
if isinstance(event, Code_Event_Types.TextChangedEvent):
|
if isinstance(event, Code_Event_Types.TextChangedEvent):
|
||||||
if not self.signal_mapper.active_view: return
|
|
||||||
self.signal_mapper.active_view.command.exec("update_info_bar")
|
self.signal_mapper.active_view.command.exec("update_info_bar")
|
||||||
elif isinstance(event, Code_Event_Types.SetActiveFileEvent):
|
elif isinstance(event, Code_Event_Types.SetActiveFileEvent):
|
||||||
self.signal_mapper.active_view.set_buffer(
|
self.signal_mapper.set_buffer_to_active_view(event.buffer)
|
||||||
event.buffer
|
|
||||||
)
|
|
||||||
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
|
elif isinstance(event, Code_Event_Types.TextInsertedEvent):
|
||||||
self.signal_mapper.insert_text(event.file, event.text)
|
self.signal_mapper.insert_text(event.file, event.text)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class SourceBuffer(GtkSource.Buffer):
|
|||||||
def set_signals(
|
def set_signals(
|
||||||
self,
|
self,
|
||||||
_changed,
|
_changed,
|
||||||
|
_after_changed,
|
||||||
_mark_set,
|
_mark_set,
|
||||||
_insert_text,
|
_insert_text,
|
||||||
_after_insert_text,
|
_after_insert_text,
|
||||||
@@ -34,6 +35,7 @@ class SourceBuffer(GtkSource.Buffer):
|
|||||||
|
|
||||||
self._handler_ids = [
|
self._handler_ids = [
|
||||||
self.connect("changed", _changed),
|
self.connect("changed", _changed),
|
||||||
|
self.connect_after("changed", _after_changed),
|
||||||
self.connect("mark-set", _mark_set),
|
self.connect("mark-set", _mark_set),
|
||||||
self.connect("insert-text", _insert_text),
|
self.connect("insert-text", _insert_text),
|
||||||
self.connect_after("insert-text", _after_insert_text),
|
self.connect_after("insert-text", _after_insert_text),
|
||||||
@@ -43,20 +45,26 @@ class SourceBuffer(GtkSource.Buffer):
|
|||||||
def block_changed_signal(self):
|
def block_changed_signal(self):
|
||||||
self.handler_block(self._handler_ids[0])
|
self.handler_block(self._handler_ids[0])
|
||||||
|
|
||||||
|
def block_changed_after_signal(self):
|
||||||
|
self.handler_block(self._handler_ids[1])
|
||||||
|
|
||||||
def block_insert_after_signal(self):
|
def block_insert_after_signal(self):
|
||||||
self.handler_block(self._handler_ids[3])
|
self.handler_block(self._handler_ids[4])
|
||||||
|
|
||||||
def block_modified_changed_signal(self):
|
def block_modified_changed_signal(self):
|
||||||
self.handler_block(self._handler_ids[4])
|
self.handler_block(self._handler_ids[5])
|
||||||
|
|
||||||
def unblock_changed_signal(self):
|
def unblock_changed_signal(self):
|
||||||
self.handler_unblock(self._handler_ids[0])
|
self.handler_unblock(self._handler_ids[0])
|
||||||
|
|
||||||
|
def unblock_changed_after_signal(self):
|
||||||
|
self.handler_unblock(self._handler_ids[1])
|
||||||
|
|
||||||
def unblock_insert_after_signal(self):
|
def unblock_insert_after_signal(self):
|
||||||
self.handler_unblock(self._handler_ids[3])
|
self.handler_unblock(self._handler_ids[4])
|
||||||
|
|
||||||
def unblock_modified_changed_signal(self):
|
def unblock_modified_changed_signal(self):
|
||||||
self.handler_unblock(self._handler_ids[4])
|
self.handler_unblock(self._handler_ids[5])
|
||||||
|
|
||||||
def clear_signals(self):
|
def clear_signals(self):
|
||||||
for handle_id in self._handler_ids:
|
for handle_id in self._handler_ids:
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ 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.buffer: SourceBuffer = SourceBuffer()
|
self.buffer: SourceBuffer = SourceBuffer()
|
||||||
|
|
||||||
self._set_signals()
|
self._set_signals()
|
||||||
@@ -36,6 +35,7 @@ class SourceFile(GtkSource.File):
|
|||||||
def _set_signals(self):
|
def _set_signals(self):
|
||||||
self.buffer.set_signals(
|
self.buffer.set_signals(
|
||||||
self._changed,
|
self._changed,
|
||||||
|
self._after_changed,
|
||||||
self._mark_set,
|
self._mark_set,
|
||||||
self._insert_text,
|
self._insert_text,
|
||||||
self._after_insert_text,
|
self._after_insert_text,
|
||||||
@@ -43,6 +43,9 @@ class SourceFile(GtkSource.File):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _changed(self, buffer: SourceBuffer):
|
def _changed(self, buffer: SourceBuffer):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _after_changed(self, buffer: SourceBuffer):
|
||||||
self.check_file_on_disk()
|
self.check_file_on_disk()
|
||||||
|
|
||||||
event = Event_Factory.create_event(
|
event = Event_Factory.create_event(
|
||||||
@@ -53,7 +56,7 @@ class SourceFile(GtkSource.File):
|
|||||||
self.emit(event)
|
self.emit(event)
|
||||||
|
|
||||||
if self.is_deleted():
|
if self.is_deleted():
|
||||||
print("deleted")
|
print("is_deleted")
|
||||||
# event = Event_Factory.create_event("file_deleted", buffer = buffer)
|
# event = Event_Factory.create_event("file_deleted", buffer = buffer)
|
||||||
# event.file = self
|
# event.file = self
|
||||||
# self.emit(event)
|
# self.emit(event)
|
||||||
@@ -138,6 +141,7 @@ class SourceFile(GtkSource.File):
|
|||||||
undo_manager.begin_not_undoable_action()
|
undo_manager.begin_not_undoable_action()
|
||||||
self.buffer.insert_at_cursor(data)
|
self.buffer.insert_at_cursor(data)
|
||||||
undo_manager.end_not_undoable_action()
|
undo_manager.end_not_undoable_action()
|
||||||
|
self.buffer.set_modified(False)
|
||||||
|
|
||||||
def set_path(self, gfile: Gio.File):
|
def set_path(self, gfile: Gio.File):
|
||||||
if not gfile: return
|
if not gfile: return
|
||||||
@@ -150,13 +154,15 @@ class SourceFile(GtkSource.File):
|
|||||||
self.emit(event)
|
self.emit(event)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
self._write_file( self.get_location() )
|
||||||
|
|
||||||
|
self.buffer.set_modified(False)
|
||||||
event = Event_Factory.create_event(
|
event = Event_Factory.create_event(
|
||||||
"saved_file",
|
"saved_file",
|
||||||
file = self, buffer = self.buffer
|
file = self, buffer = self.buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
self.emit(event)
|
self.emit(event)
|
||||||
self._write_file( self.get_location() )
|
|
||||||
|
|
||||||
def save_as(self):
|
def save_as(self):
|
||||||
file = event_system.emit_and_await("save-file-dialog")
|
file = event_system.emit_and_await("save-file-dialog")
|
||||||
|
|||||||
@@ -65,19 +65,6 @@ class TabsWidget(Gtk.Notebook):
|
|||||||
|
|
||||||
self.message(event)
|
self.message(event)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def _bind_tab_menu(self, tab, page_widget):
|
def _bind_tab_menu(self, tab, page_widget):
|
||||||
def do_context_menu(tab, eve, page_widget):
|
def do_context_menu(tab, eve, page_widget):
|
||||||
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
|
if eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
|
||||||
@@ -116,6 +103,29 @@ class TabsWidget(Gtk.Notebook):
|
|||||||
|
|
||||||
return context_menu
|
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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
if buffer.get_modified():
|
||||||
|
ctx.add_class("file-changed")
|
||||||
|
else:
|
||||||
|
ctx.remove_class("file-changed")
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user