Refactor controller architecture and multi-insert state
- Rename ControllerContext to ControllerMessageBus for clarity - Switch ControllerBase to use SingletonRaised - Replace MarkEventsMixin with MarkerManager for multi-cursor editing - Add populate_popup event support for source view context menus - Remove unused swap file events - Moved JSON prettify feature to plugin - Fix event name: "removed_file" -> "remove_file"
This commit is contained in:
3
plugins/code/prettify_json/__init__.py
Normal file
3
plugins/code/prettify_json/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
||||
3
plugins/code/prettify_json/__main__.py
Normal file
3
plugins/code/prettify_json/__main__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Package
|
||||
"""
|
||||
7
plugins/code/prettify_json/manifest.json
Normal file
7
plugins/code/prettify_json/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Prettify JSON",
|
||||
"author": "ITDominator",
|
||||
"version": "0.0.1",
|
||||
"support": "",
|
||||
"requests": {}
|
||||
}
|
||||
55
plugins/code/prettify_json/plugin.py
Normal file
55
plugins/code/prettify_json/plugin.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Python imports
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
|
||||
from plugins.plugin_types import PluginCode
|
||||
|
||||
|
||||
|
||||
def _load_prettify_json(buffer, menu):
|
||||
menu.append( Gtk.SeparatorMenuItem() )
|
||||
|
||||
def on_prettify_json(menuitem, buffer):
|
||||
start_itr, \
|
||||
end_itr = buffer.get_start_iter(), buffer.get_end_iter()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
text = json.dumps(json.loads(data), separators = (',', ':'), indent = 4)
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text)
|
||||
buffer.end_user_action()
|
||||
|
||||
item = Gtk.MenuItem(label = "Prettify JSON")
|
||||
item.connect("activate", on_prettify_json, buffer)
|
||||
menu.append(item)
|
||||
|
||||
|
||||
|
||||
class Plugin(PluginCode):
|
||||
def __init__(self):
|
||||
super(Plugin, self).__init__()
|
||||
|
||||
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.PopulateSourceViewPopupEvent):
|
||||
language = event.buffer.get_language()
|
||||
if not language: return
|
||||
|
||||
if language.get_id() == "json":
|
||||
_load_prettify_json(event.buffer, event.menu)
|
||||
|
||||
def load(self):
|
||||
...
|
||||
|
||||
def run(self):
|
||||
...
|
||||
@@ -86,7 +86,7 @@ class CommandSystem:
|
||||
|
||||
def remove_file(self, view: SourceView):
|
||||
event = Event_Factory.create_event(
|
||||
"removed_file",
|
||||
"remove_file",
|
||||
view = view,
|
||||
buffer = view.get_buffer()
|
||||
)
|
||||
|
||||
@@ -20,8 +20,6 @@ class FilesController(ControllerBase, list):
|
||||
def _controller_message(self, event: Code_Event_Types.CodeEvent):
|
||||
if isinstance(event, Code_Event_Types.AddNewFileEvent):
|
||||
self.new_file(event)
|
||||
elif isinstance(event, Code_Event_Types.SwapFileEvent):
|
||||
self.swap_file(event)
|
||||
elif isinstance(event, Code_Event_Types.PopFileEvent):
|
||||
self.pop_file(event)
|
||||
elif isinstance(event, Code_Event_Types.RemoveFileEvent):
|
||||
@@ -31,29 +29,6 @@ class FilesController(ControllerBase, list):
|
||||
elif isinstance(event, Code_Event_Types.GetSwapFileEvent):
|
||||
self.get_swap_file(event)
|
||||
|
||||
def get_file(self, event: Code_Event_Types.GetFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
for file in self:
|
||||
if not event.buffer == file.buffer: continue
|
||||
|
||||
event.response = file
|
||||
|
||||
return file
|
||||
|
||||
def get_swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
for i, file in enumerate(self):
|
||||
if not event.buffer == file.buffer: continue
|
||||
|
||||
j = self.next_index(i)
|
||||
next_file = self[j]
|
||||
swapped_file = self[j] if not j == -1 else None
|
||||
|
||||
event.response = [swapped_file, next_file]
|
||||
|
||||
return swapped_file, next_file
|
||||
|
||||
def new_file(self, event: Code_Event_Types.AddNewFileEvent):
|
||||
file = SourceFile()
|
||||
@@ -73,20 +48,6 @@ class FilesController(ControllerBase, list):
|
||||
|
||||
return file
|
||||
|
||||
def swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
for i, file in enumerate(self):
|
||||
if not event.buffer == file.buffer: continue
|
||||
|
||||
j = self.next_index(i)
|
||||
next_file = self[j]
|
||||
swapped_file = self[j] if not j == -1 else None
|
||||
|
||||
event.response = [swapped_file, next_file]
|
||||
|
||||
return swapped_file, next_file
|
||||
|
||||
def pop_file(self, event: Code_Event_Types.PopFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
@@ -134,6 +95,30 @@ class FilesController(ControllerBase, list):
|
||||
|
||||
return next_file
|
||||
|
||||
def get_file(self, event: Code_Event_Types.GetFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
for file in self:
|
||||
if not event.buffer == file.buffer: continue
|
||||
|
||||
event.response = file
|
||||
|
||||
return file
|
||||
|
||||
def get_swap_file(self, event: Code_Event_Types.GetSwapFileEvent):
|
||||
if not event.buffer: return
|
||||
|
||||
for i, file in enumerate(self):
|
||||
if not event.buffer == file.buffer: continue
|
||||
|
||||
j = self.next_index(i)
|
||||
next_file = self[j]
|
||||
swapped_file = self[j] if not j == -1 else None
|
||||
|
||||
event.response = [swapped_file, next_file]
|
||||
|
||||
return swapped_file, next_file
|
||||
|
||||
def next_index(self, i):
|
||||
size = len(self)
|
||||
|
||||
|
||||
137
src/core/widgets/code/controllers/views/marker_manager.py
Normal file
137
src/core/widgets/code/controllers/views/marker_manager.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from ...mixins.mark_support_mixin import MarkSupportMixin
|
||||
|
||||
|
||||
|
||||
class MarkerManager(MarkSupportMixin):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.buffer_markers: dict = {}
|
||||
|
||||
self.selection_tag: Gtk.TextTag = Gtk.TextTag.new("selection")
|
||||
self.selection_tag.props.background = "rgba(111, 168, 220, 0.64)"
|
||||
self.selection_tag.props.foreground = "#ffffff"
|
||||
|
||||
|
||||
def move_by_char(self, buffer, is_forward: bool, is_selection: bool):
|
||||
self._move(buffer, is_forward, is_selection, mode = "char")
|
||||
|
||||
def move_by_word(self, buffer, is_forward: bool, is_selection: bool):
|
||||
self._move(buffer, is_forward, is_selection, mode = "word")
|
||||
|
||||
def move_by_line(self, buffer, is_forward: bool, is_selection: bool):
|
||||
self._move(buffer, is_forward, is_selection, mode = "line")
|
||||
|
||||
def _move(self, buffer, is_forward: bool, is_selection: bool, mode: str):
|
||||
self.clear_highlight(buffer)
|
||||
self.insert_selection_tag(buffer)
|
||||
|
||||
for mark_hash in self.buffer_markers:
|
||||
marker = self.buffer_markers[mark_hash]
|
||||
start_mark = marker["start_mark"]
|
||||
end_mark = marker["end_mark"]
|
||||
has_selection = marker["is_selection"]
|
||||
|
||||
start_itr = buffer.get_iter_at_mark(start_mark)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
if is_selection:
|
||||
self.buffer_markers[mark_hash]["is_selection"] = True
|
||||
|
||||
self._move_iter(buffer, end_itr, mode, is_forward)
|
||||
buffer.move_mark(end_mark, end_itr)
|
||||
|
||||
self._apply_selection(buffer, start_itr, end_itr)
|
||||
continue
|
||||
|
||||
if has_selection:
|
||||
self.collapse_selection(buffer, mark_hash, start_mark, end_mark, is_forward)
|
||||
continue
|
||||
|
||||
# No selection — move both anchor and caret together
|
||||
self._move_iter(buffer, end_itr, mode, is_forward)
|
||||
|
||||
buffer.move_mark(start_mark, end_itr)
|
||||
buffer.move_mark(end_mark, end_itr)
|
||||
|
||||
def collapse_selection(self,
|
||||
buffer, mark_hash, start_mark, end_mark, is_forward: bool
|
||||
):
|
||||
self.buffer_markers[mark_hash]["is_selection"] = False
|
||||
|
||||
start_itr = buffer.get_iter_at_mark(start_mark)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
# Determine which side is visually the caret
|
||||
if start_itr.compare(end_itr) <= 0:
|
||||
left = start_itr
|
||||
right = end_itr
|
||||
else:
|
||||
left = end_itr
|
||||
right = start_itr
|
||||
|
||||
# If moving forward → collapse to right edge
|
||||
collapse_itr = right if is_forward else left
|
||||
|
||||
buffer.move_mark(start_mark, collapse_itr)
|
||||
buffer.move_mark(end_mark, collapse_itr)
|
||||
|
||||
def _move_iter(self, buffer, itr_, mode: str, is_forward: bool):
|
||||
if mode == "char":
|
||||
itr_.forward_char() if is_forward else itr_.backward_char()
|
||||
elif mode == "word":
|
||||
itr_.forward_word_end() if is_forward else itr_.backward_word_start()
|
||||
elif mode == "line":
|
||||
line = itr_.get_line()
|
||||
offset = itr_.get_line_offset()
|
||||
|
||||
max_line = buffer.get_line_count() - 1
|
||||
new_line = line + 1 if is_forward else line - 1
|
||||
new_line = max(0, min(max_line, new_line))
|
||||
|
||||
itr_.set_line(new_line)
|
||||
self.move_to_offset(offset, itr_)
|
||||
|
||||
def _apply_selection(self, buffer, start_itr, end_itr):
|
||||
if start_itr.compare(end_itr) <= 0:
|
||||
buffer.apply_tag(self.selection_tag, start_itr, end_itr)
|
||||
else:
|
||||
buffer.apply_tag(self.selection_tag, end_itr, start_itr)
|
||||
|
||||
|
||||
def button_release_event(self, source_view, event):
|
||||
buffer = source_view.get_buffer()
|
||||
|
||||
coords = source_view.window_to_buffer_coords(
|
||||
Gtk.TextWindowType.TEXT,
|
||||
event.x,
|
||||
event.y,
|
||||
)
|
||||
|
||||
is_over_text, target_itr, _ = source_view.get_iter_at_position(
|
||||
coords.buffer_x,
|
||||
coords.buffer_y,
|
||||
)
|
||||
|
||||
if not is_over_text:
|
||||
target_itr.forward_visible_line()
|
||||
target_itr.backward_char()
|
||||
|
||||
if self.remove_mark_set(target_itr, buffer):
|
||||
return
|
||||
|
||||
self.insert_mark_set(target_itr, buffer)
|
||||
|
||||
def key_press_event(self, source_view, event, key_mapper):
|
||||
...
|
||||
@@ -42,7 +42,8 @@ class SourceViewSignalMapper:
|
||||
"key-press-event": self._key_press_event,
|
||||
"key-release-event": self._key_release_event,
|
||||
"button-press-event": self._button_press_event,
|
||||
"button-release-event": self._button_release_event
|
||||
"button-release-event": self._button_release_event,
|
||||
"populate-popup": self._populate_popup
|
||||
}
|
||||
|
||||
def _focus_in_event(self, source_view: SourceView, eve):
|
||||
@@ -54,14 +55,17 @@ class SourceViewSignalMapper:
|
||||
source_view, step, count, extend_selection, self.emit
|
||||
)
|
||||
|
||||
def _key_press_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_key_press_event(source_view, eve)
|
||||
|
||||
def _key_release_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_key_release_event(source_view, eve)
|
||||
|
||||
def _button_press_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_button_press_event(source_view, eve)
|
||||
|
||||
def _button_release_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_button_release_event(source_view, eve)
|
||||
|
||||
def _key_press_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_key_press_event(source_view, eve)
|
||||
|
||||
def _key_release_event(self, source_view: SourceView, eve):
|
||||
return self.state_manager.handle_key_release_event(source_view, eve)
|
||||
def _populate_popup(self, source_view, menu):
|
||||
return self.state_manager.handle_populate_popup(source_view, menu, self.emit)
|
||||
|
||||
@@ -34,15 +34,6 @@ class SourceViewStateManager:
|
||||
source_view, step, count, extend_selection, emit
|
||||
)
|
||||
|
||||
def handle_button_press_event(self, source_view, eve):
|
||||
return self.states[source_view.state].button_press_event(source_view, eve)
|
||||
|
||||
def handle_button_release_event(self, source_view, eve):
|
||||
# Handle state transitions (multi-insert toggling)
|
||||
self._handle_multi_insert_toggle(source_view, eve)
|
||||
|
||||
return self.states[source_view.state].button_release_event(source_view, eve)
|
||||
|
||||
def handle_key_press_event(self, source_view, eve):
|
||||
return self.states[source_view.state].key_press_event(
|
||||
source_view, eve, self.key_mapper
|
||||
@@ -53,6 +44,17 @@ class SourceViewStateManager:
|
||||
source_view, eve, self.key_mapper
|
||||
)
|
||||
|
||||
def handle_button_press_event(self, source_view, eve):
|
||||
self._handle_multi_insert_toggle(source_view, eve)
|
||||
|
||||
return self.states[source_view.state].button_press_event(source_view, eve)
|
||||
|
||||
def handle_button_release_event(self, source_view, eve):
|
||||
return self.states[source_view.state].button_release_event(source_view, eve)
|
||||
|
||||
def handle_populate_popup(self, source_view, menu, emit):
|
||||
return self.states[source_view.state].populate_popup(source_view, menu, emit)
|
||||
|
||||
def _handle_multi_insert_toggle(self, source_view, eve):
|
||||
is_control = self.key_mapper.is_control(eve)
|
||||
if is_control and not source_view.state == SourceViewStates.MULTIINSERT:
|
||||
@@ -61,6 +63,7 @@ class SourceViewStateManager:
|
||||
|
||||
if not is_control and source_view.state == SourceViewStates.MULTIINSERT:
|
||||
logger.debug("Entered Regular Insert Mode...")
|
||||
self.states[source_view.state].clear_markers(source_view)
|
||||
self.states[source_view.state].marker_manager.clear_mark_sets(source_view)
|
||||
|
||||
source_view.state = SourceViewStates.INSERT
|
||||
|
||||
|
||||
@@ -78,3 +78,15 @@ class SourceViewsBaseState:
|
||||
)
|
||||
|
||||
return True if not response else response
|
||||
|
||||
def populate_popup(self, source_view, menu, emit):
|
||||
buffer = source_view.get_buffer()
|
||||
event = Event_Factory.create_event(
|
||||
"populate_source_view_popup",
|
||||
buffer = buffer,
|
||||
menu = menu
|
||||
)
|
||||
|
||||
emit(event)
|
||||
|
||||
menu.show_all()
|
||||
|
||||
@@ -2,140 +2,108 @@
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.event_factory import Event_Factory, Code_Event_Types
|
||||
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
|
||||
from libs.event_factory import Event_Factory
|
||||
from libs.dto.states import CursorAction
|
||||
|
||||
from ....mixins.source_mark_events_mixin import MarkEventsMixin
|
||||
from ..marker_manager import MarkerManager
|
||||
|
||||
from .source_view_base_state import SourceViewsBaseState
|
||||
|
||||
|
||||
|
||||
class SourceViewsMultiInsertState(SourceViewsBaseState, MarkEventsMixin):
|
||||
class SourceViewsMultiInsertState(SourceViewsBaseState):
|
||||
def __init__(self):
|
||||
super(SourceViewsMultiInsertState, self).__init__()
|
||||
|
||||
self.cursor_action: CursorAction = 0
|
||||
self.move_direction: MoveDirection = 0
|
||||
self.insert_markers: list = []
|
||||
self.cursor_action: CursorAction = None
|
||||
self.marker_manager: MarkerManager = MarkerManager()
|
||||
|
||||
|
||||
def insert_text(self, file, text):
|
||||
if not self.insert_markers: return False
|
||||
def insert_text(self, file, text: str) -> bool:
|
||||
if not self.marker_manager.buffer_markers: return False
|
||||
|
||||
buffer = file.buffer
|
||||
|
||||
if buffer.is_processing_completion:
|
||||
self.insert_completion_text(buffer, text)
|
||||
return True
|
||||
return self._insert_completion_text(buffer, text)
|
||||
|
||||
# freeze buffer and insert to each mark (if any)
|
||||
buffer.block_insert_after_signal()
|
||||
buffer.begin_user_action()
|
||||
def insert_text(start_itr, end_itr = None):
|
||||
if not end_itr:
|
||||
buffer.insert(start_itr, text, -1)
|
||||
return
|
||||
|
||||
with buffer.freeze_notify():
|
||||
for mark in self.insert_markers:
|
||||
itr = buffer.get_iter_at_mark(mark)
|
||||
buffer.insert(itr, text, -1)
|
||||
|
||||
buffer.end_user_action()
|
||||
buffer.unblock_insert_after_signal()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text, -1)
|
||||
|
||||
self.marker_manager.apply_to_marks(buffer, insert_text)
|
||||
return True
|
||||
|
||||
def insert_completion_text(self, buffer, text):
|
||||
def _insert_completion_text(self, buffer, text: str) -> bool:
|
||||
buffer.is_processing_completion = False
|
||||
|
||||
# freeze buffer and insert to each mark (if any)
|
||||
buffer.block_insert_after_signal()
|
||||
buffer.begin_user_action()
|
||||
def replace_word(start_itr, end_itr = None):
|
||||
if not end_itr:
|
||||
end_itr = start_itr.copy()
|
||||
|
||||
with buffer.freeze_notify():
|
||||
for mark in self.insert_markers:
|
||||
end_itr = buffer.get_iter_at_mark(mark)
|
||||
start_itr = end_itr.copy()
|
||||
if not start_itr.starts_word():
|
||||
start_itr.backward_word_start()
|
||||
|
||||
if not start_itr.starts_word():
|
||||
start_itr.backward_word_start()
|
||||
if not end_itr.ends_word():
|
||||
end_itr.forward_word_end()
|
||||
|
||||
if not end_itr.ends_word():
|
||||
end_itr.forward_word_end()
|
||||
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text, -1)
|
||||
|
||||
buffer.end_user_action()
|
||||
buffer.unblock_insert_after_signal()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text, -1)
|
||||
|
||||
self.marker_manager.apply_to_marks(buffer, replace_word)
|
||||
return True
|
||||
|
||||
def move_cursor(self, source_view, step, count, extend_selection, emit):
|
||||
buffer = source_view.get_buffer()
|
||||
|
||||
self._process_move_direction(buffer)
|
||||
def move_cursor(self, source_view, step, count, is_selection, emit):
|
||||
is_forward = count > 0
|
||||
buffer = source_view.get_buffer()
|
||||
|
||||
if step in [
|
||||
Gtk.MovementStep.LOGICAL_POSITIONS,
|
||||
Gtk.MovementStep.VISUAL_POSITIONS
|
||||
]:
|
||||
self.marker_manager.move_by_char(buffer, is_forward, is_selection)
|
||||
elif step == Gtk.MovementStep.WORDS:
|
||||
self.marker_manager.move_by_word(buffer, is_forward, is_selection)
|
||||
elif step == Gtk.MovementStep.DISPLAY_LINES:
|
||||
self.marker_manager.move_by_line(buffer, is_forward, is_selection)
|
||||
|
||||
self._signal_cursor_moved(source_view, emit)
|
||||
source_view.command.exec("update_info_bar")
|
||||
|
||||
def button_press_event(self, source_view, eve):
|
||||
super().button_press_event(source_view, eve)
|
||||
def key_press_event(self, source_view, event, key_mapper):
|
||||
char = key_mapper.get_raw_keyname(event).upper()
|
||||
self.is_control = key_mapper.is_control(event)
|
||||
self.is_shift = key_mapper.is_shift(event)
|
||||
|
||||
if char.upper() in ["BACKSPACE", "DELETE", "ENTER"]:
|
||||
self.marker_manager.process_cursor_action(
|
||||
source_view.get_buffer(),
|
||||
char.upper()
|
||||
)
|
||||
return False
|
||||
|
||||
return super().key_press_event(source_view, event, key_mapper)
|
||||
|
||||
def button_press_event(self, source_view, event):
|
||||
return True
|
||||
|
||||
def button_release_event(self, source_view, eve):
|
||||
buffer = source_view.get_buffer()
|
||||
insert_iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
data = source_view.window_to_buffer_coords(
|
||||
Gtk.TextWindowType.TEXT,
|
||||
eve.x,
|
||||
eve.y
|
||||
)
|
||||
is_over_text, \
|
||||
target_iter, \
|
||||
is_trailing = source_view.get_iter_at_position(data.buffer_x, data.buffer_y)
|
||||
|
||||
if not is_over_text:
|
||||
# NOTE: Trying to put at very end of line if not over text (aka, clicking right of text)
|
||||
target_iter.forward_visible_line()
|
||||
target_iter.backward_char()
|
||||
|
||||
self._insert_mark(insert_iter, target_iter, buffer)
|
||||
|
||||
def key_press_event(self, source_view, eve, key_mapper):
|
||||
char = key_mapper.get_raw_keyname(eve)
|
||||
|
||||
for action in CursorAction:
|
||||
if not action.name == char.upper(): continue
|
||||
self.cursor_action = action.value
|
||||
self._process_cursor_action(source_view.get_buffer())
|
||||
|
||||
return False
|
||||
|
||||
for direction in MoveDirection:
|
||||
if not direction.name == char.upper(): continue
|
||||
self.move_direction = direction.value
|
||||
return False
|
||||
|
||||
is_future = key_mapper._key_release_event(eve)
|
||||
if is_future: return True
|
||||
|
||||
command = key_mapper._key_press_event(eve)
|
||||
if not command: return False
|
||||
|
||||
char_str = key_mapper.get_char(eve)
|
||||
modkeys_states = key_mapper.get_modkeys_states(eve)
|
||||
response = source_view.command.exec_with_args(
|
||||
command, source_view, char_str, modkeys_states
|
||||
)
|
||||
|
||||
return True if not response else response
|
||||
def button_release_event(self, source_view, event):
|
||||
self.marker_manager.button_release_event(source_view, event)
|
||||
|
||||
def _signal_cursor_moved(self, source_view, emit):
|
||||
buffer = source_view.get_buffer()
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = itr.get_line()
|
||||
char = itr.get_line_offset()
|
||||
|
||||
event = Event_Factory.create_event(
|
||||
"cursor_moved",
|
||||
view = source_view,
|
||||
@@ -145,3 +113,4 @@ class SourceViewsMultiInsertState(SourceViewsBaseState, MarkEventsMixin):
|
||||
)
|
||||
|
||||
emit(event)
|
||||
|
||||
|
||||
134
src/core/widgets/code/mixins/mark_support_mixin.py
Normal file
134
src/core/widgets/code/mixins/mark_support_mixin.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# Python imports
|
||||
import random
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class MarkSupportMixin:
|
||||
def clear_mark_sets(self, source_view):
|
||||
buffer = source_view.get_buffer()
|
||||
self.clear_highlight(buffer)
|
||||
|
||||
for mark_set in self.buffer_markers.values():
|
||||
start_mark, end_mark, is_selection = mark_set.values()
|
||||
start_mark.set_visible(False)
|
||||
buffer.delete_mark(start_mark)
|
||||
buffer.delete_mark(end_mark)
|
||||
|
||||
self.buffer_markers.clear()
|
||||
|
||||
def insert_selection_tag(self, buffer):
|
||||
tag_table = buffer.get_tag_table()
|
||||
if not tag_table.lookup("selection"):
|
||||
tag_table.add(self.selection_tag)
|
||||
|
||||
def clear_highlight(self, buffer):
|
||||
if not self.selection_tag: return
|
||||
start_itr, end_itr = buffer.get_bounds()
|
||||
buffer.remove_tag(self.selection_tag, start_itr, end_itr)
|
||||
|
||||
def apply_to_marks(self, buffer, operation):
|
||||
buffer.block_insert_after_signal()
|
||||
buffer.begin_user_action()
|
||||
|
||||
try:
|
||||
with buffer.freeze_notify():
|
||||
for mark_hash in self.buffer_markers:
|
||||
marker = self.buffer_markers[mark_hash]
|
||||
start_mark = marker["start_mark"]
|
||||
end_mark = marker["end_mark"]
|
||||
has_selection = marker["is_selection"]
|
||||
|
||||
start_itr = buffer.get_iter_at_mark(start_mark)
|
||||
end_itr = buffer.get_iter_at_mark(end_mark)
|
||||
|
||||
if has_selection:
|
||||
operation(start_itr, end_itr)
|
||||
self.collapse_selection(
|
||||
buffer, mark_hash, start_mark, end_mark, False
|
||||
)
|
||||
else:
|
||||
operation(start_itr)
|
||||
finally:
|
||||
buffer.end_user_action()
|
||||
buffer.unblock_insert_after_signal()
|
||||
|
||||
def process_cursor_action(self, buffer, action):
|
||||
def remove_text(start_itr, end_itr = None):
|
||||
if end_itr:
|
||||
buffer.delete(start_itr, end_itr)
|
||||
return
|
||||
|
||||
buffer.backspace(start_itr, interactive = True, default_editable = True)
|
||||
|
||||
def delete_text(start_itr, end_itr = None):
|
||||
if end_itr:
|
||||
buffer.delete(start_itr, end_itr)
|
||||
return
|
||||
|
||||
start_itr.forward_char()
|
||||
buffer.backspace(start_itr, interactive = True, default_editable = True)
|
||||
|
||||
if action == "BACKSPACE":
|
||||
self.apply_to_marks(buffer, remove_text)
|
||||
elif action == "DELETE":
|
||||
self.apply_to_marks(buffer, delete_text)
|
||||
elif action == "ENTER":
|
||||
...
|
||||
|
||||
def move_to_offset(self, offset, start_itr):
|
||||
line_itr = start_itr.copy()
|
||||
|
||||
line_itr.forward_to_line_end()
|
||||
|
||||
next_line_length = line_itr.get_line_offset()
|
||||
new_offset = min(offset, next_line_length)
|
||||
start_itr.set_line_offset(new_offset)
|
||||
|
||||
def insert_mark_set(self, target_iter, buffer):
|
||||
random_bits = random.getrandbits(128)
|
||||
hash = "%032x" % random_bits
|
||||
|
||||
start_mark = Gtk.TextMark.new(
|
||||
name = f"multi-insert-start-{hash}",
|
||||
left_gravity = False
|
||||
)
|
||||
|
||||
end_mark = Gtk.TextMark.new(
|
||||
name = f"multi-insert-end-{hash}",
|
||||
left_gravity = False
|
||||
)
|
||||
# left_gravity = True
|
||||
|
||||
buffer.add_mark(start_mark, target_iter)
|
||||
buffer.add_mark(end_mark, target_iter)
|
||||
start_mark.set_visible(True)
|
||||
self.buffer_markers[f"{hash}"] = {
|
||||
"start_mark": start_mark,
|
||||
"end_mark": end_mark,
|
||||
"is_selection": False
|
||||
}
|
||||
|
||||
def remove_mark_set(self, target_iter, buffer) -> bool:
|
||||
marks = target_iter.get_marks()
|
||||
|
||||
for mark_hash in self.buffer_markers:
|
||||
start_mark, end_mark, is_selection = self.buffer_markers[mark_hash].values()
|
||||
if not start_mark in marks: continue
|
||||
|
||||
start_mark.set_visible(False)
|
||||
buffer.delete_mark(start_mark)
|
||||
buffer.delete_mark(end_mark)
|
||||
del self.buffer_markers[mark_hash]
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -1,107 +0,0 @@
|
||||
# Python imports
|
||||
import random
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from libs.dto.states import SourceViewStates, MoveDirection, CursorAction
|
||||
|
||||
|
||||
|
||||
class MarkEventsMixin:
|
||||
def clear_markers(self, source_view):
|
||||
buffer = source_view.get_buffer()
|
||||
|
||||
for mark in self.insert_markers:
|
||||
mark.set_visible(False)
|
||||
buffer.delete_mark(mark)
|
||||
|
||||
self.insert_markers.clear()
|
||||
|
||||
def _insert_mark(self, insert_iter, target_iter, buffer):
|
||||
mark_found = self._check_for_insert_marks(target_iter, buffer)
|
||||
|
||||
if mark_found: return
|
||||
|
||||
random_bits = random.getrandbits(128)
|
||||
hash = "%032x" % random_bits
|
||||
|
||||
mark = Gtk.TextMark.new(
|
||||
name = f"multi_insert_{hash}",
|
||||
left_gravity = False
|
||||
)
|
||||
|
||||
buffer.add_mark(mark, target_iter)
|
||||
mark.set_visible(True)
|
||||
self.insert_markers.append(mark)
|
||||
|
||||
|
||||
def _check_for_insert_marks(self, target_iter, buffer):
|
||||
marks = target_iter.get_marks()
|
||||
|
||||
for mark in marks:
|
||||
if mark in self.insert_markers[:]:
|
||||
mark.set_visible(False)
|
||||
self.insert_markers.remove(mark)
|
||||
buffer.delete_mark(mark)
|
||||
return True
|
||||
|
||||
insert_itr = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
if target_iter.equal(insert_itr): return True
|
||||
|
||||
return False
|
||||
|
||||
def _process_cursor_action(self, buffer):
|
||||
if not self.insert_markers: return
|
||||
if self.cursor_action == CursorAction.NONE.value: return
|
||||
|
||||
action = self.cursor_action
|
||||
for mark in self.insert_markers:
|
||||
itr = buffer.get_iter_at_mark(mark)
|
||||
|
||||
if action == CursorAction.BACKSPACE.value:
|
||||
buffer.backspace(itr, interactive = True, default_editable = True)
|
||||
elif action == CursorAction.DELETE.value:
|
||||
itr.forward_char()
|
||||
buffer.backspace(itr, interactive = True, default_editable = True)
|
||||
elif action == CursorAction.ENTER.value:
|
||||
...
|
||||
|
||||
self.cursor_action = CursorAction.NONE.value
|
||||
|
||||
def _process_move_direction(self, buffer):
|
||||
if not self.insert_markers: return
|
||||
if self.move_direction == MoveDirection.NONE.value: return
|
||||
|
||||
direction = self.move_direction
|
||||
for mark in self.insert_markers:
|
||||
itr = buffer.get_iter_at_mark(mark)
|
||||
|
||||
if direction == MoveDirection.UP.value:
|
||||
new_line = itr.get_line() - 1
|
||||
new_itr = buffer.get_iter_at_line_offset(
|
||||
new_line,
|
||||
itr.get_line_index()
|
||||
)
|
||||
|
||||
elif direction == MoveDirection.DOWN.value:
|
||||
new_line = itr.get_line() + 1
|
||||
new_itr = buffer.get_iter_at_line_offset(
|
||||
new_line,
|
||||
itr.get_line_index()
|
||||
)
|
||||
elif direction == MoveDirection.LEFT.value:
|
||||
if not itr.backward_char(): break
|
||||
new_itr = itr
|
||||
elif direction == MoveDirection.RIGHT.value:
|
||||
if not itr.forward_char(): break
|
||||
new_itr = itr
|
||||
else:
|
||||
continue
|
||||
|
||||
buffer.move_mark_by_name(mark.get_name(), new_itr)
|
||||
|
||||
self.move_direction = MoveDirection.NONE
|
||||
@@ -58,7 +58,6 @@ class SourceView(GtkSource.View, SourceViewDnDMixin):
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("drag-data-received", self._on_drag_data_received)
|
||||
self.connect("populate-popup", self._on_populate_popup)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
@@ -76,37 +75,6 @@ class SourceView(GtkSource.View, SourceViewDnDMixin):
|
||||
|
||||
self._set_up_dnd()
|
||||
|
||||
def _on_populate_popup(self, view, menu):
|
||||
buffer = self.get_buffer()
|
||||
language = buffer.get_language()
|
||||
|
||||
if language.get_id() == "json":
|
||||
self._load_prettify_json(view, menu)
|
||||
|
||||
menu.show_all()
|
||||
|
||||
def _load_prettify_json(self, view, menu):
|
||||
menu.append( Gtk.SeparatorMenuItem() )
|
||||
|
||||
def on_prettify_json(menuitem):
|
||||
import json
|
||||
|
||||
buffer = self.get_buffer()
|
||||
start_itr, \
|
||||
end_itr = buffer.get_start_iter(), buffer.get_end_iter()
|
||||
data = buffer.get_text(start_itr, end_itr, False)
|
||||
text = json.dumps(json.loads(data), separators = (',', ':'), indent = 4)
|
||||
|
||||
buffer.begin_user_action()
|
||||
buffer.delete(start_itr, end_itr)
|
||||
buffer.insert(start_itr, text)
|
||||
buffer.end_user_action()
|
||||
|
||||
item = Gtk.MenuItem(label = "Prettify JSON")
|
||||
item.connect("activate", on_prettify_json)
|
||||
menu.append(item)
|
||||
|
||||
|
||||
def clear_temp_cut_buffer_delayed(self):
|
||||
if self._cut_temp_timeout_id:
|
||||
GLib.source_remove(self._cut_temp_timeout_id)
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..singleton import Singleton
|
||||
from ..singleton_raised import SingletonRaised
|
||||
|
||||
from ..dto.base_event import BaseEvent
|
||||
|
||||
from .emit_dispatcher import EmitDispatcher
|
||||
from .controller_context import ControllerContext
|
||||
from .controller_message_bus import ControllerMessageBus
|
||||
|
||||
|
||||
|
||||
@@ -17,28 +17,28 @@ class ControllerBaseException(Exception):
|
||||
|
||||
|
||||
|
||||
class ControllerBase(Singleton, EmitDispatcher):
|
||||
class ControllerBase(SingletonRaised, EmitDispatcher):
|
||||
def __init__(self):
|
||||
super(ControllerBase, self).__init__()
|
||||
|
||||
self.controller_context: ControllerContext = None
|
||||
self.controller_message_bus: ControllerMessageBus = None
|
||||
|
||||
|
||||
def _controller_message(self, event: BaseEvent):
|
||||
raise ControllerBaseException("Controller Base '_controller_message' must be overridden...")
|
||||
|
||||
def set_controller_context(self, controller_context: ControllerContext):
|
||||
self.controller_context = controller_context
|
||||
def set_controller_message_bus(self, controller_message_bus: ControllerMessageBus):
|
||||
self.controller_message_bus = controller_message_bus
|
||||
|
||||
def message(self, event: BaseEvent):
|
||||
return self.controller_context.message(event)
|
||||
return self.controller_message_bus.message(event)
|
||||
|
||||
def message_to(self, name: str, event: BaseEvent):
|
||||
return self.controller_context.message_to(name, event)
|
||||
return self.controller_message_bus.message_to(name, event)
|
||||
|
||||
def message_to_selected(self, names: list[str], event: BaseEvent):
|
||||
for name in names:
|
||||
self.controller_context.message_to_selected(name, event)
|
||||
self.controller_message_bus.message_to_selected(name, event)
|
||||
|
||||
def register_controller(self, name: str, controller):
|
||||
self.controller_context.register_controller(name, controller)
|
||||
self.controller_message_bus.register_controller(name, controller)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..dto.base_event import BaseEvent
|
||||
|
||||
|
||||
|
||||
class ControllerContextException(Exception):
|
||||
...
|
||||
|
||||
|
||||
|
||||
class ControllerContext:
|
||||
def __init__(self):
|
||||
super(ControllerContext, self).__init__()
|
||||
|
||||
|
||||
def message(self, event: BaseEvent):
|
||||
raise ControllerContextException("Controller Context 'message' must be overriden by Controller Manager...")
|
||||
|
||||
def message_to(self, name: str, event: BaseEvent):
|
||||
raise ControllerContextException("Controller Context 'message_to' must be overriden by Controller Manager...")
|
||||
|
||||
def message_to_selected(self, name: list, event: BaseEvent):
|
||||
raise ControllerContextException("Controller Context 'message_to_selected' must be overriden by Controller Manager...")
|
||||
|
||||
def register_controller(self, name: str, controller):
|
||||
raise ControllerContextException("Controller Context 'register_controller' must be overriden by Controller Manager...")
|
||||
@@ -7,7 +7,7 @@ from ..singleton import Singleton
|
||||
from ..event_factory import Code_Event_Types
|
||||
|
||||
from .controller_base import ControllerBase
|
||||
from .controller_context import ControllerContext
|
||||
from .controller_message_bus import ControllerMessageBus
|
||||
|
||||
|
||||
|
||||
@@ -17,17 +17,26 @@ class ControllerManagerException(Exception):
|
||||
|
||||
|
||||
class ControllerManager(Singleton, dict):
|
||||
"""
|
||||
ControllerManager registers controllers by key/value pair.
|
||||
It binds the message bus methods methods each controller has
|
||||
due to extending ControllerBase.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(ControllerManager, self).__init__()
|
||||
|
||||
self.message_bus: ControllerMessageBus \
|
||||
= self._crete_controller_message_bus()
|
||||
|
||||
def _crete_controller_context(self) -> ControllerContext:
|
||||
controller_context = ControllerContext()
|
||||
controller_context.message_to = self.message_to
|
||||
controller_context.message = self.message
|
||||
controller_context.register_controller = self.register_controller
|
||||
|
||||
return controller_context
|
||||
def _crete_controller_message_bus(self) -> ControllerMessageBus:
|
||||
controller_message_bus = ControllerMessageBus()
|
||||
controller_message_bus.message_to = self.message_to
|
||||
controller_message_bus.message = self.message
|
||||
controller_message_bus.register_controller = self.register_controller
|
||||
|
||||
return controller_message_bus
|
||||
|
||||
def register_controller(self, name: str, controller: ControllerBase):
|
||||
if not name or controller == None:
|
||||
@@ -38,7 +47,7 @@ class ControllerManager(Singleton, dict):
|
||||
f"Can't bind controller to existing registered name of '{name}'..."
|
||||
)
|
||||
|
||||
controller.set_controller_context( self._crete_controller_context() )
|
||||
controller.set_controller_message_bus( self.message_bus )
|
||||
|
||||
self[name] = controller
|
||||
|
||||
|
||||
30
src/libs/controllers/controller_message_bus.py
Normal file
30
src/libs/controllers/controller_message_bus.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..dto.base_event import BaseEvent
|
||||
|
||||
|
||||
|
||||
class ControllerMessageBusException(Exception):
|
||||
...
|
||||
|
||||
|
||||
|
||||
class ControllerMessageBus:
|
||||
def __init__(self):
|
||||
super(ControllerMessageBus, self).__init__()
|
||||
|
||||
|
||||
def message(self, event: BaseEvent):
|
||||
raise ControllerMessageBusException("Controller Message Bus 'message' must be overriden by Controller Manager...")
|
||||
|
||||
def message_to(self, name: str, event: BaseEvent):
|
||||
raise ControllerMessageBusException("Controller Message Bus 'message_to' must be overriden by Controller Manager...")
|
||||
|
||||
def message_to_selected(self, name: list, event: BaseEvent):
|
||||
raise ControllerMessageBusException("Controller Message Bus 'message_to_selected' must be overriden by Controller Manager...")
|
||||
|
||||
def register_controller(self, name: str, controller):
|
||||
raise ControllerMessageBusException("Controller Message Bus 'register_controller' must be overriden by Controller Manager...")
|
||||
@@ -8,6 +8,13 @@ from ..dto.base_event import BaseEvent
|
||||
|
||||
|
||||
class EmitDispatcher:
|
||||
"""
|
||||
EmitDispatcher is used for allowing controllers to pass/hook in
|
||||
their message system to children that need to signal events.
|
||||
Note how we are not handling return info from the 'message' methods
|
||||
whereas a controller would or could do so.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(EmitDispatcher, self).__init__()
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from dataclasses import dataclass, field
|
||||
|
||||
@dataclass(slots = True)
|
||||
class BaseEvent:
|
||||
topic: str = None
|
||||
content: any = None
|
||||
raw_content: any = None
|
||||
topic: str = None
|
||||
content: any = None
|
||||
raw_content: any = None
|
||||
success: callable = None
|
||||
fail: callable = None
|
||||
|
||||
@@ -9,6 +9,7 @@ from .register_command_event import RegisterCommandEvent
|
||||
from .file_externally_modified_event import FileExternallyModifiedEvent
|
||||
from .file_externally_deleted_event import FileExternallyDeletedEvent
|
||||
from .set_info_labels_event import SetInfoLabelsEvent
|
||||
from .populate_source_view_popup_event import PopulateSourceViewPopupEvent
|
||||
|
||||
from .get_new_command_system_event import GetNewCommandSystemEvent
|
||||
from .request_completion_event import RequestCompletionEvent
|
||||
@@ -21,7 +22,6 @@ from .set_active_file_event import SetActiveFileEvent
|
||||
|
||||
from .file_path_set_event import FilePathSetEvent
|
||||
from .added_new_file_event import AddedNewFileEvent
|
||||
from .swapped_file_event import SwappedFileEvent
|
||||
from .popped_file_event import PoppedFileEvent
|
||||
from .removed_file_event import RemovedFileEvent
|
||||
from .saved_file_event import SavedFileEvent
|
||||
@@ -29,6 +29,5 @@ from .saved_file_event import SavedFileEvent
|
||||
from .get_file_event import GetFileEvent
|
||||
from .get_swap_file_event import GetSwapFileEvent
|
||||
from .add_new_file_event import AddNewFileEvent
|
||||
from .swap_file_event import SwapFileEvent
|
||||
from .pop_file_event import PopFileEvent
|
||||
from .remove_file_event import RemoveFileEvent
|
||||
|
||||
18
src/libs/dto/code/populate_source_view_popup_event.py
Normal file
18
src/libs/dto/code/populate_source_view_popup_event.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from .code_event import CodeEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class PopulateSourceViewPopupEvent(CodeEvent):
|
||||
menu: Gtk.Widget = None
|
||||
@@ -1,13 +0,0 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .code_event import CodeEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SwapFileEvent(CodeEvent):
|
||||
...
|
||||
@@ -1,13 +0,0 @@
|
||||
# Python imports
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .code_event import CodeEvent
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class SwappedFileEvent(CodeEvent):
|
||||
...
|
||||
@@ -4,4 +4,3 @@
|
||||
|
||||
from .source_view_states import SourceViewStates
|
||||
from .cursor_action import CursorAction
|
||||
from .move_direction import MoveDirection
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Python imports
|
||||
from enum import Enum
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class MoveDirection(Enum):
|
||||
NONE = 0
|
||||
UP = 1
|
||||
DOWN = 2
|
||||
LEFT = 3
|
||||
RIGHT = 4
|
||||
@@ -15,18 +15,18 @@ class SingletonError(Exception):
|
||||
T = TypeVar('T', bound='Singleton')
|
||||
|
||||
class Singleton:
|
||||
_instance = None
|
||||
__instance = None
|
||||
|
||||
def __new__(cls: Type[T], *args: Any, **kwargs: Any) -> T:
|
||||
if cls._instance is not None:
|
||||
if cls.__instance is not None:
|
||||
logger.debug(f"'{cls.__name__}' is a Singleton. Returning instance...")
|
||||
return cls._instance
|
||||
return cls.__instance
|
||||
|
||||
cls._instance = super(Singleton, cls).__new__(cls)
|
||||
return cls._instance
|
||||
cls.__instance = super(Singleton, cls).__new__(cls)
|
||||
return cls.__instance
|
||||
|
||||
def __init__(self) -> None:
|
||||
if self._instance is not None:
|
||||
if self.__instance is not None:
|
||||
return
|
||||
|
||||
super(Singleton, self).__init__()
|
||||
|
||||
@@ -15,15 +15,15 @@ class SingletonError(Exception):
|
||||
T = TypeVar('T', bound='SingletonRaised')
|
||||
|
||||
class SingletonRaised:
|
||||
_instance = None
|
||||
__instance = None
|
||||
|
||||
def __new__(cls: Type[T], *args: Any, **kwargs: Any) -> T:
|
||||
if cls._instance is not None:
|
||||
if cls.__instance is not None:
|
||||
raise SingletonError(f"'{cls.__name__}' is a Singleton. Cannot create a new instance...")
|
||||
|
||||
cls._instance = super(SingletonRaised, cls).__new__(cls)
|
||||
return cls._instance
|
||||
cls.__instance = super(SingletonRaised, cls).__new__(cls)
|
||||
return cls.__instance
|
||||
|
||||
def __init__(self) -> None:
|
||||
if self._instance is not None:
|
||||
if self.__instance is not None:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user