generated from itdominator/Python-With-Gtk-Template
Refactored mixins; linked most buttons; added more keybindings
This commit is contained in:
parent
f17e92d148
commit
d98982bc86
|
@ -6,7 +6,6 @@ gi.require_version('Gtk', '3.0')
|
|||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from ..widgets.base.notebook.editor_notebook import EditorNotebook
|
||||
from ..widgets.base.webkit.editor import Editor
|
||||
|
||||
|
||||
|
@ -34,10 +33,7 @@ class EditorsPaned(Gtk.Paned):
|
|||
|
||||
def _load_widgets(self):
|
||||
self.add1( Editor() )
|
||||
self.add2( Editor() )
|
||||
|
||||
# self.add1( EditorNotebook() )
|
||||
# self.add2( EditorNotebook() )
|
||||
# self.add2( Editor() )
|
||||
|
||||
def _update_paned_handle(self):
|
||||
rect = self.get_allocation()
|
||||
|
|
|
@ -10,17 +10,16 @@ from gi.repository import Gdk
|
|||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from ..mixins.signals_mixins import SignalsMixins
|
||||
from libs.mixins.keyboard_signals_mixin import KeyboardSignalsMixin
|
||||
|
||||
from ..containers.base_container import BaseContainer
|
||||
# from ..containers.core_widget import CoreWidget
|
||||
|
||||
from .base_controller_data import BaseControllerData
|
||||
from .bridge_controller import BridgeController
|
||||
|
||||
|
||||
|
||||
class BaseController(SignalsMixins, BaseControllerData):
|
||||
class BaseController(KeyboardSignalsMixin, BaseControllerData):
|
||||
def __init__(self, args, unknownargs):
|
||||
messages = []
|
||||
for arg in unknownargs + [args.new_tab,]:
|
||||
|
|
|
@ -11,6 +11,7 @@ class BridgeController:
|
|||
def __init__(self):
|
||||
|
||||
self.opened_files = {}
|
||||
self.originator = -1
|
||||
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
|
@ -21,9 +22,27 @@ class BridgeController:
|
|||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("handle_bridge_event", self.handle_bridge_event)
|
||||
event_system.subscribe(f"keyboard_open_file", self.keyboard_open_file)
|
||||
event_system.subscribe(f"keyboard_scale_up_text", self.keyboard_scale_up_text)
|
||||
event_system.subscribe(f"keyboard_scale_down_text", self.keyboard_scale_down_text)
|
||||
event_system.subscribe(f"toggle_highlight_line", self.toggle_highlight_line)
|
||||
|
||||
def keyboard_open_file(self, gfiles):
|
||||
event_system.emit(f"set_pre_drop_dnd_{self.originator}", (gfiles,))
|
||||
|
||||
def keyboard_scale_up_text(self):
|
||||
event_system.emit(f"keyboard_scale_up_text_{self.originator}")
|
||||
|
||||
def keyboard_scale_down_text(self):
|
||||
event_system.emit(f"keyboard_scale_down_text_{self.originator}")
|
||||
|
||||
def toggle_highlight_line(self):
|
||||
event_system.emit(f"toggle_highlight_line_{self.originator}")
|
||||
|
||||
|
||||
def handle_bridge_event(self, event):
|
||||
self.originator = event.originator
|
||||
|
||||
match event.topic:
|
||||
case "save":
|
||||
event_system.emit(f"handle_file_event_{event.originator}", (event,))
|
||||
|
@ -35,6 +54,15 @@ class BridgeController:
|
|||
event_system.emit(f"handle_file_event_{event.originator}", (event,))
|
||||
case "open_file":
|
||||
event_system.emit(f"handle_file_event_{event.originator}", (event,))
|
||||
case "tggl_top_main_menubar":
|
||||
event_system.emit("tggl_top_main_menubar")
|
||||
case "set_info_labels":
|
||||
content = base64.b64decode( event.content.encode() ).decode("utf-8")
|
||||
path = event.fpath
|
||||
line_char = content
|
||||
file_type = event.ftype
|
||||
encoding_type = "utf-8"
|
||||
event_system.emit(f"set_info_labels", (path, line_char, file_type, encoding_type,))
|
||||
case "error":
|
||||
content = base64.b64decode( event.content.encode() ).decode("utf-8")
|
||||
logger.info(content)
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Mixins module
|
||||
"""
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Signals module
|
||||
"""
|
|
@ -1,12 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
from .signals.keyboard_signals_mixin import KeyboardSignalsMixin
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
|
||||
class SignalsMixins(KeyboardSignalsMixin):
|
||||
...
|
|
@ -35,7 +35,7 @@ class GeneralInfoWidget:
|
|||
...
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("set_bottom_labels", self.set_bottom_labels)
|
||||
event_system.subscribe("set_info_labels", self.set_info_labels)
|
||||
event_system.subscribe("set_path_label", self._set_path_label)
|
||||
event_system.subscribe("set_encoding_label", self._set_encoding_label)
|
||||
event_system.subscribe("set_line_char_label", self._set_line_char_label)
|
||||
|
@ -63,7 +63,7 @@ class GeneralInfoWidget:
|
|||
builder.get_object("core_widget").add(self.bottom_status_info)
|
||||
|
||||
|
||||
def set_bottom_labels(self, path = None, line_char = None, file_type = None, encoding_type = None):
|
||||
def set_info_labels(self, path = None, line_char = None, file_type = None, encoding_type = None):
|
||||
self._set_path_label(path)
|
||||
self._set_line_char_label(line_char)
|
||||
self._set_file_type_label(file_type)
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Notebook Module
|
||||
"""
|
|
@ -1,33 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .key_input_controller import KeyInputController
|
||||
from .editor_events import EditorEventsMixin
|
||||
|
||||
|
||||
|
||||
class EditorControllerMixin(KeyInputController, EditorEventsMixin):
|
||||
def get_active_view(self):
|
||||
page_num = self.get_current_page()
|
||||
container = self.get_nth_page( page_num )
|
||||
source_view = container.get_source_view()
|
||||
return page_num, container, source_view
|
||||
|
||||
def action_controller(self, action = "", query = ""):
|
||||
# NOTE: Not efficent here as multiple same calls
|
||||
if not self.is_editor_focused: # TODO: Find way to converge this
|
||||
return
|
||||
|
||||
page_num, container, source_view = self.get_active_view()
|
||||
|
||||
# NOTE: These feel bad being here man...
|
||||
if action == "scale_up_text":
|
||||
self.scale_up_text(source_view)
|
||||
if action == "scale_down_text":
|
||||
self.scale_down_text(source_view)
|
||||
if action == "set_buffer_language":
|
||||
self.set_buffer_language(source_view, query)
|
||||
if action == "set_buffer_style":
|
||||
self.set_buffer_style(source_view, query)
|
|
@ -1,160 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from ..sourceview_container import SourceViewContainer
|
||||
|
||||
|
||||
|
||||
class EditorEventsMixin:
|
||||
def create_view(self, widget = None, eve = None, gfile = None, line: int = 0):
|
||||
container = SourceViewContainer(self.close_tab)
|
||||
page_num = self.append_page(container, container.get_tab_widget())
|
||||
|
||||
self.set_tab_detachable(container, True)
|
||||
self.set_tab_reorderable(container, True)
|
||||
|
||||
self.show_all()
|
||||
self.set_current_page(page_num)
|
||||
|
||||
if gfile:
|
||||
source_view = container.get_source_view()
|
||||
source_view.open_file(gfile, line)
|
||||
source_view.grab_focus()
|
||||
|
||||
def open_file(self, gfile):
|
||||
page_num = self.get_current_page()
|
||||
container = self.get_nth_page( page_num )
|
||||
source_view = container.get_source_view()
|
||||
|
||||
if source_view._current_filename == "":
|
||||
source_view.open_file(gfile)
|
||||
else:
|
||||
self.create_view(None, None, gfile)
|
||||
|
||||
# Note: Need to get parent instead given we pass the close_tab method
|
||||
# from a potentially former notebook.
|
||||
def close_tab(self, button, container, source_view, eve = None):
|
||||
notebook = container.get_parent()
|
||||
if notebook.NAME == "notebook_1" and notebook.get_n_pages() == 1:
|
||||
return
|
||||
|
||||
file_type = source_view.get_filetype()
|
||||
if not file_type == "buffer":
|
||||
uri = source_view.get_current_file().get_uri()
|
||||
event_system.emit("textDocument/didClose", (file_type, uri,))
|
||||
|
||||
page_num = notebook.page_num(container)
|
||||
source_view._cancel_current_file_watchers()
|
||||
notebook.remove_page(page_num)
|
||||
|
||||
if notebook.NAME == "notebook_2" and notebook.get_n_pages() == 0:
|
||||
notebook.hide()
|
||||
event_system.emit("focused_target_changed", ("notebook_1",))
|
||||
|
||||
def keyboard_prev_tab(self, page_num):
|
||||
page_num = self.get_n_pages() - 1 if page_num == 0 else page_num - 1
|
||||
self.set_current_page(page_num)
|
||||
|
||||
def keyboard_next_tab(self, page_num):
|
||||
page_num = 0 if self.get_n_pages() - 1 == page_num else page_num + 1
|
||||
self.set_current_page(page_num)
|
||||
|
||||
def keyboard_focus_1st_pane(self):
|
||||
if self.NAME == "notebook_1":
|
||||
return
|
||||
|
||||
notebook = self.builder.get_object("notebook_1")
|
||||
i = notebook.get_current_page()
|
||||
page = notebook.get_nth_page(i)
|
||||
|
||||
self.set_page_focus(page, notebook, self)
|
||||
|
||||
def keyboard_focus_2nd_pane(self):
|
||||
if self.NAME == "notebook_2":
|
||||
return
|
||||
|
||||
notebook = self.builder.get_object("notebook_2")
|
||||
if not notebook.is_visible():
|
||||
notebook.show()
|
||||
notebook.create_view()
|
||||
event_system.emit("update_paned_handle")
|
||||
|
||||
i = notebook.get_current_page()
|
||||
page = notebook.get_nth_page(i)
|
||||
|
||||
self.set_page_focus(page, notebook, self)
|
||||
|
||||
def keyboard_move_tab_to_1(self, page_num):
|
||||
if self.NAME == "notebook_1": return
|
||||
|
||||
notebook = self.builder.get_object("notebook_1")
|
||||
page = self.get_nth_page(page_num)
|
||||
tab = page.get_tab_widget()
|
||||
|
||||
self.detach_tab(page)
|
||||
notebook.show()
|
||||
notebook.insert_page(page, tab, -1)
|
||||
|
||||
if self.get_n_pages() == 0:
|
||||
self.hide()
|
||||
|
||||
self.set_page_focus(page, notebook, self)
|
||||
|
||||
def keyboard_move_tab_to_2(self, page_num):
|
||||
if self.NAME == "notebook_2":
|
||||
return
|
||||
|
||||
if self.NAME == "notebook_1" and self.get_n_pages() == 1:
|
||||
return
|
||||
|
||||
notebook = self.builder.get_object("notebook_2")
|
||||
page = self.get_nth_page(page_num)
|
||||
tab = page.get_tab_widget()
|
||||
|
||||
self.detach_tab(page)
|
||||
if not notebook.is_visible():
|
||||
notebook.show()
|
||||
event_system.emit("update_paned_handle")
|
||||
|
||||
notebook.insert_page(page, tab, -1)
|
||||
|
||||
self.set_page_focus(page, notebook, self)
|
||||
|
||||
def set_page_focus(self, page, notebook, old_notebook):
|
||||
old_notebook.is_editor_focused = False
|
||||
notebook.set_current_page(-1)
|
||||
page.get_children()[0].grab_focus()
|
||||
notebook.is_editor_focused = True
|
||||
|
||||
ctx = old_notebook.get_style_context()
|
||||
ctx.remove_class("notebook-selected-focus")
|
||||
|
||||
ctx = notebook.get_style_context()
|
||||
ctx.add_class("notebook-selected-focus")
|
||||
|
||||
def keyboard_move_tab_left(self, page_num):
|
||||
page = self.get_nth_page(page_num)
|
||||
page_num = self.get_n_pages() - 1 if page_num == 0 else page_num - 1
|
||||
self.reorder_child(page, page_num)
|
||||
|
||||
def keyboard_move_tab_right(self, page_num):
|
||||
page = self.get_nth_page(page_num)
|
||||
page_num = 0 if self.get_n_pages() - 1 == page_num else page_num + 1
|
||||
self.reorder_child(page, page_num)
|
||||
|
||||
|
||||
# NOTE: These feel bad being here man...
|
||||
def scale_up_text(self, source_view):
|
||||
source_view.scale_up_text()
|
||||
|
||||
def scale_down_text(self, source_view):
|
||||
source_view.scale_down_text()
|
||||
|
||||
def set_buffer_language(self, source_view, language = "python3"):
|
||||
source_view.set_buffer_language(language)
|
||||
|
||||
def set_buffer_style(self, source_view, style = settings.theming.syntax_theme):
|
||||
buffer = source_view.get_buffer()
|
||||
source_view.set_buffer_style(buffer, style)
|
|
@ -1,142 +0,0 @@
|
|||
# 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
|
||||
from gi.repository import Gio
|
||||
|
||||
# Application imports
|
||||
from .editor_controller import EditorControllerMixin
|
||||
|
||||
|
||||
|
||||
# NOTE: https://github.com/Axel-Erfurt/TextEdit/tree/b65f09be945196eb05bef83d81a6abcd129b4eb0
|
||||
class EditorNotebook(EditorControllerMixin, Gtk.Notebook):
|
||||
ccount = 0
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
obj = super(EditorNotebook, cls).__new__(cls)
|
||||
cls.ccount += 1
|
||||
|
||||
return obj
|
||||
|
||||
def __init__(self):
|
||||
super(EditorNotebook, self).__init__()
|
||||
|
||||
self.NAME = f"notebook_{self.ccount}"
|
||||
self.builder = settings_manager.get_builder()
|
||||
self.is_editor_focused = False
|
||||
|
||||
self.set_group_name("editor_widget")
|
||||
self.builder.expose_object(self.NAME, self)
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show_all()
|
||||
|
||||
if self.NAME == "notebook_1":
|
||||
self.is_editor_focused = True
|
||||
|
||||
if self.NAME == "notebook_2":
|
||||
self.hide()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_scrollable(True)
|
||||
self.set_vexpand(True)
|
||||
self.set_hexpand(True)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("switch-page", self._switch_page_update)
|
||||
self.connect("key-press-event", self._key_press_event)
|
||||
self.connect("key-release-event", self._key_release_event)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe("create_view", self._create_view)
|
||||
event_system.subscribe("set_buffer_style", self.action_controller)
|
||||
event_system.subscribe("set_buffer_language", self.action_controller)
|
||||
event_system.subscribe("focused_target_changed", self._focused_target_changed)
|
||||
|
||||
event_system.subscribe("keyboard_open_file", self._keyboard_open_file)
|
||||
event_system.subscribe("keyboard_scale_up_text", self._keyboard_scale_up_text)
|
||||
event_system.subscribe("keyboard_scale_down_text", self._keyboard_scale_down_text)
|
||||
event_system.subscribe("keyboard_focus_1st_pane", self.keyboard_focus_1st_pane)
|
||||
event_system.subscribe("keyboard_focus_2nd_pane", self.keyboard_focus_2nd_pane)
|
||||
|
||||
def _load_widgets(self):
|
||||
self._add_action_widgets()
|
||||
if self.NAME == "notebook_1" and not settings_manager.is_starting_with_file():
|
||||
self.create_view()
|
||||
|
||||
def _dbl_click_create_view(self, notebook, eve):
|
||||
if eve.type == Gdk.EventType.DOUBLE_BUTTON_PRESS and eve.button == 1: # l-click
|
||||
...
|
||||
|
||||
def _focused_target_changed(self, target):
|
||||
self.is_editor_focused = True if target == self.NAME else False
|
||||
if self.is_editor_focused:
|
||||
self.grab_focus()
|
||||
|
||||
def _add_action_widgets(self):
|
||||
start_box = Gtk.Box()
|
||||
end_box = Gtk.Box()
|
||||
|
||||
add_btn = Gtk.Button()
|
||||
add_btn.set_image( Gtk.Image.new_from_icon_name("add", 4) )
|
||||
add_btn.set_always_show_image(True)
|
||||
add_btn.connect("released", self.create_view)
|
||||
|
||||
end_box.add(add_btn)
|
||||
|
||||
start_box.show_all()
|
||||
end_box.show_all()
|
||||
|
||||
# PACKTYPE: 0 Start, 1 = End
|
||||
self.set_action_widget(start_box, 0)
|
||||
self.set_action_widget(end_box, 1)
|
||||
|
||||
def _switch_page_update(self, notebook, page, page_num):
|
||||
source_view = page.get_source_view()
|
||||
gfile = source_view.get_current_file()
|
||||
|
||||
if not gfile:
|
||||
event_system.emit("set_path_label", ("",))
|
||||
event_system.emit("set_file_type_label", (source_view._current_filetype,))
|
||||
else:
|
||||
source_view.load_file_info(gfile)
|
||||
source_view.update_cursor_position()
|
||||
source_view.set_bottom_labels(gfile)
|
||||
|
||||
event_system.emit(f"set_source_view", (source_view,))
|
||||
|
||||
def _create_view(self, gfile = None, line: int = 0):
|
||||
if not self.is_editor_focused: # TODO: Find way to converge this
|
||||
return
|
||||
|
||||
if isinstance(gfile, str):
|
||||
parts = gfile.split(":")
|
||||
gfile = Gio.File.new_for_path(parts[0])
|
||||
try:
|
||||
line = int(parts[1]) if len(parts) > 1 else 0
|
||||
except Exception:
|
||||
...
|
||||
|
||||
self.create_view(None, None, gfile, line)
|
||||
|
||||
def _keyboard_open_file(self, gfile):
|
||||
if not self.is_editor_focused: # TODO: Find way to converge this
|
||||
return
|
||||
|
||||
self.open_file(gfile)
|
||||
|
||||
def _keyboard_scale_up_text(self):
|
||||
self.action_controller("scale_up_text")
|
||||
|
||||
def _keyboard_scale_down_text(self):
|
||||
self.action_controller("scale_down_text")
|
|
@ -1,69 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class KeyInputController:
|
||||
def _key_press_event(self, widget, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
|
||||
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
|
||||
|
||||
try:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False
|
||||
except Exception:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.MOD1_MASK else False
|
||||
|
||||
|
||||
def _key_release_event(self, widget, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
|
||||
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
|
||||
|
||||
try:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False
|
||||
except Exception:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.MOD1_MASK else False
|
||||
|
||||
page_num, container, source_view = self.get_active_view()
|
||||
if is_control:
|
||||
if is_shift:
|
||||
if keyname in ["Up", "Down"]:
|
||||
if keyname == "Up":
|
||||
self.keyboard_move_tab_to_1(page_num)
|
||||
if keyname == "Down":
|
||||
self.keyboard_move_tab_to_2(page_num)
|
||||
|
||||
return True
|
||||
|
||||
if keyname in ["w", "t", "o"]:
|
||||
if keyname == "w":
|
||||
self.close_tab(None, container, source_view)
|
||||
if keyname == "t":
|
||||
self._create_view()
|
||||
if keyname == "o":
|
||||
page_num, container, source_view = self.get_active_view()
|
||||
event_system.emit("open_files", (source_view,))
|
||||
|
||||
return True
|
||||
|
||||
if is_alt:
|
||||
if keyname in ["Up", "Down", "Left", "Right"]:
|
||||
if keyname == "Up":
|
||||
self.keyboard_prev_tab(page_num)
|
||||
if keyname == "Down":
|
||||
self.keyboard_next_tab(page_num)
|
||||
if keyname == "Left":
|
||||
self.keyboard_move_tab_left(page_num)
|
||||
if keyname == "Right":
|
||||
self.keyboard_move_tab_right(page_num)
|
||||
|
||||
return True
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
SourceView Module
|
||||
"""
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Custom Completion Providers Module
|
||||
"""
|
|
@ -1,74 +0,0 @@
|
|||
# Python imports
|
||||
import re
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class ExampleCompletionProvider(GObject.GObject, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This is a custom Completion Example Provider.
|
||||
# NOTE: used information from here --> https://warroom.rsmus.com/do-that-auto-complete/
|
||||
"""
|
||||
__gtype_name__ = 'CustomProvider'
|
||||
|
||||
def __init__(self):
|
||||
GObject.Object.__init__(self)
|
||||
|
||||
def do_get_name(self):
|
||||
""" Returns: a new string containing the name of the provider. """
|
||||
return _('ExampleProvider')
|
||||
|
||||
def do_match(self, context):
|
||||
""" Get whether the provider match the context of completion detailed in context. """
|
||||
# NOTE: True for debugging but context needs to normally get checked for actual usage needs.
|
||||
# TODO: Fix me
|
||||
return True
|
||||
|
||||
def do_populate(self, context):
|
||||
"""
|
||||
In this instance, it will do 2 things:
|
||||
1) always provide Hello World! (Not ideal but an option so its in the example)
|
||||
2) Utilizes the Gtk.TextIter from the TextBuffer to determine if there is a jinja
|
||||
example of '{{ custom.' if so it will provide you with the options of foo and bar.
|
||||
If selected it will insert foo }} or bar }}, completing your syntax...
|
||||
|
||||
PLEASE NOTE the GtkTextIter Logic and regex are really rough and should be adjusted and tuned
|
||||
"""
|
||||
|
||||
proposals = [
|
||||
# GtkSource.CompletionItem(label='Hello World!', text = 'Hello World!', icon = None, info = None) # NOTE: Always proposed...
|
||||
]
|
||||
|
||||
# Gtk Versions differ on get_iter responses...
|
||||
end_iter = context.get_iter()
|
||||
if not isinstance(end_iter, Gtk.TextIter):
|
||||
_, end_iter = context.get_iter()
|
||||
|
||||
if end_iter:
|
||||
buf = end_iter.get_buffer()
|
||||
mov_iter = end_iter.copy()
|
||||
if mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY):
|
||||
mov_iter, _ = mov_iter.backward_search('{{', Gtk.TextSearchFlags.VISIBLE_ONLY)
|
||||
left_text = buf.get_text(mov_iter, end_iter, True)
|
||||
else:
|
||||
left_text = ''
|
||||
|
||||
if re.match(r'.*\{\{\s*custom\.$', left_text):
|
||||
proposals.append(
|
||||
GtkSource.CompletionItem(label='foo', text='foo }}') # optionally proposed based on left search via regex
|
||||
)
|
||||
proposals.append(
|
||||
GtkSource.CompletionItem(label='bar', text='bar }}') # optionally proposed based on left search via regex
|
||||
)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
|
@ -1,91 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class LSPCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This code is an LSP code completion plugin for Newton.
|
||||
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
|
||||
"""
|
||||
__gtype_name__ = 'LSPProvider'
|
||||
|
||||
def __init__(self, source_view):
|
||||
GObject.Object.__init__(self)
|
||||
|
||||
self._theme = Gtk.IconTheme.get_default()
|
||||
self._source_view = source_view
|
||||
|
||||
|
||||
def do_get_name(self):
|
||||
return "LSP Code Completion"
|
||||
|
||||
def get_iter_correctly(self, context):
|
||||
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
|
||||
|
||||
def do_match(self, context):
|
||||
iter = self.get_iter_correctly(context)
|
||||
buffer = iter.get_buffer()
|
||||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
return False
|
||||
|
||||
event_system.emit("textDocument/completion", (self._source_view, context, self.do_populate))
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 1
|
||||
|
||||
def do_get_activation(self):
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context, result = None):
|
||||
proposals = []
|
||||
if result:
|
||||
if not result.items is None:
|
||||
for item in result.items:
|
||||
proposals.append( self.create_completion_item(item) )
|
||||
else:
|
||||
proposals.append( self.create_completion_item(result) )
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
|
||||
def get_icon_for_type(self, _type):
|
||||
try:
|
||||
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
|
||||
except:
|
||||
...
|
||||
|
||||
try:
|
||||
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
|
||||
except:
|
||||
...
|
||||
|
||||
return None
|
||||
|
||||
def create_completion_item(self, item):
|
||||
comp_item = GtkSource.CompletionItem.new()
|
||||
comp_item.set_label(item.label)
|
||||
|
||||
if item.textEdit:
|
||||
if isinstance(item.textEdit, dict):
|
||||
comp_item.set_text(item.textEdit["newText"])
|
||||
else:
|
||||
comp_item.set_text(item.textEdit)
|
||||
else:
|
||||
comp_item.set_text(item.insertText)
|
||||
|
||||
comp_item.set_icon( self.get_icon_for_type(item.kind) )
|
||||
comp_item.set_info(item.documentation)
|
||||
|
||||
|
||||
return comp_item
|
|
@ -1,108 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GtkSource
|
||||
from gi.repository import GObject
|
||||
|
||||
import jedi
|
||||
from jedi.api import Script
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
# FIXME: Find real icon names...
|
||||
icon_names = {
|
||||
'import': '',
|
||||
'module': '',
|
||||
'class': '',
|
||||
'function': '',
|
||||
'statement': '',
|
||||
'param': ''
|
||||
}
|
||||
|
||||
|
||||
class Jedi:
|
||||
def get_script(file, doc_text):
|
||||
return Script(code = doc_text, path = file)
|
||||
|
||||
|
||||
class PythonCompletionProvider(GObject.Object, GtkSource.CompletionProvider):
|
||||
"""
|
||||
This code is A python code completion plugin for Newton.
|
||||
# NOTE: Some code pulled/referenced from here --> https://github.com/isamert/gedi
|
||||
"""
|
||||
__gtype_name__ = 'PythonProvider'
|
||||
|
||||
def __init__(self, file):
|
||||
GObject.Object.__init__(self)
|
||||
self._theme = Gtk.IconTheme.get_default()
|
||||
self._file = file
|
||||
|
||||
def do_get_name(self):
|
||||
return "Python Code Completion"
|
||||
|
||||
def get_iter_correctly(self, context):
|
||||
return context.get_iter()[1] if isinstance(context.get_iter(), tuple) else context.get_iter()
|
||||
|
||||
def do_match(self, context):
|
||||
iter = self.get_iter_correctly(context)
|
||||
iter.backward_char()
|
||||
|
||||
buffer = iter.get_buffer()
|
||||
if buffer.get_context_classes_at_iter(iter) != ['no-spell-check']:
|
||||
return False
|
||||
|
||||
ch = iter.get_char()
|
||||
# NOTE: Look to re-add or apply supprting logic to use spaces
|
||||
# As is it slows down the editor in certain contexts...
|
||||
# if not (ch in ('_', '.', ' ') or ch.isalnum()):
|
||||
if not (ch in ('_', '.') or ch.isalnum()):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 1
|
||||
|
||||
def do_get_activation(self):
|
||||
return GtkSource.CompletionActivation.INTERACTIVE
|
||||
|
||||
def do_populate(self, context):
|
||||
# TODO: Maybe convert async?
|
||||
it = self.get_iter_correctly(context)
|
||||
buffer = it.get_buffer()
|
||||
proposals = []
|
||||
|
||||
doc_text = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False)
|
||||
iter_cursor = buffer.get_iter_at_mark(buffer.get_insert())
|
||||
linenum = iter_cursor.get_line() + 1
|
||||
charnum = iter_cursor.get_line_index()
|
||||
|
||||
def create_generator():
|
||||
for completion in Jedi.get_script(self._file, doc_text).complete(line = linenum, column = None, fuzzy = False):
|
||||
comp_item = GtkSource.CompletionItem.new()
|
||||
comp_item.set_label(completion.name)
|
||||
comp_item.set_text(completion.name)
|
||||
comp_item.set_icon(self.get_icon_for_type(completion.type))
|
||||
comp_item.set_info(completion.docstring())
|
||||
yield comp_item
|
||||
|
||||
for item in create_generator():
|
||||
proposals.append(item)
|
||||
|
||||
context.add_proposals(self, proposals, True)
|
||||
|
||||
def get_icon_for_type(self, _type):
|
||||
try:
|
||||
return self._theme.load_icon(icon_names[_type.lower()], 16, 0)
|
||||
except:
|
||||
try:
|
||||
return self._theme.load_icon(Gtk.STOCK_ADD, 16, 0)
|
||||
except:
|
||||
return None
|
|
@ -1,145 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class KeyInputController:
|
||||
|
||||
# NOTE: Mostly sinking pre-bound keys here to let our keybinder control instead...
|
||||
def _key_press_event(self, widget, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
|
||||
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
|
||||
buffer = self.get_buffer()
|
||||
|
||||
try:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False
|
||||
except Exception:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.MOD1_MASK else False
|
||||
|
||||
if is_control:
|
||||
if is_shift:
|
||||
if keyname in [ "Up", "Down" ]:
|
||||
return True
|
||||
|
||||
if keyname in [ "slash", "Up", "Down", "m", "z", "y" ]:
|
||||
return True
|
||||
|
||||
if is_alt:
|
||||
if keyname in [ "Up", "Down", "Left", "Right" ]:
|
||||
return True
|
||||
|
||||
|
||||
if len(self._multi_insert_marks) > 0:
|
||||
if keyname == "BackSpace":
|
||||
self.begin_user_action(buffer)
|
||||
|
||||
with buffer.freeze_notify():
|
||||
GLib.idle_add(self._delete_on_multi_line_markers, *(buffer,))
|
||||
|
||||
self.end_user_action(buffer)
|
||||
|
||||
return True
|
||||
|
||||
# NOTE: if a plugin recieves the call and handles, it will be the final decider for propigation
|
||||
return event_system.emit_and_await("autopairs", (keyname, is_control, is_alt, is_shift))
|
||||
|
||||
|
||||
|
||||
def _key_release_event(self, widget, eve):
|
||||
if self.freeze_multi_line_insert: return
|
||||
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
modifiers = Gdk.ModifierType(eve.get_state() & ~Gdk.ModifierType.LOCK_MASK)
|
||||
is_control = True if modifiers & Gdk.ModifierType.CONTROL_MASK else False
|
||||
is_shift = True if modifiers & Gdk.ModifierType.SHIFT_MASK else False
|
||||
buffer = self.get_buffer()
|
||||
|
||||
try:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.ALT_MASK else False
|
||||
except Exception:
|
||||
is_alt = True if modifiers & Gdk.ModifierType.MOD1_MASK else False
|
||||
|
||||
can_continue = self.can_proceed(keyname, is_control, is_shift, is_alt)
|
||||
if not can_continue:
|
||||
return can_continue
|
||||
|
||||
|
||||
if is_control:
|
||||
if is_shift:
|
||||
if keyname in ["S"]:
|
||||
if keyname == "S":
|
||||
self.save_file_as()
|
||||
|
||||
return True
|
||||
|
||||
if keyname in ["z", "y", "m", "s", "h", "g", "equal", "minus", "Up", "Down"]:
|
||||
if keyname == "z":
|
||||
self.keyboard_undo()
|
||||
if keyname == "y":
|
||||
self.keyboard_redo()
|
||||
if keyname == "m":
|
||||
self.keyboard_insert_mark()
|
||||
if keyname == "s":
|
||||
self.save_file()
|
||||
if keyname == "h":
|
||||
self.toggle_highlight_line()
|
||||
if keyname == "g":
|
||||
self.go_to_call()
|
||||
|
||||
if keyname == "equal":
|
||||
self.scale_up_text()
|
||||
if keyname == "minus":
|
||||
self.scale_down_text()
|
||||
|
||||
if keyname == "Up":
|
||||
self.keyboard_move_lines_up()
|
||||
if keyname == "Down":
|
||||
self.keyboard_move_lines_down()
|
||||
|
||||
return True
|
||||
|
||||
# Note: Sink these requets
|
||||
if keyname in ["Slash"]:
|
||||
return True
|
||||
|
||||
if is_alt:
|
||||
if keyname == "m":
|
||||
self.keyboard_clear_marks()
|
||||
|
||||
|
||||
if keyname in {"Return", "Enter"}:
|
||||
if len(self._multi_insert_marks) > 0:
|
||||
self.begin_user_action(buffer)
|
||||
with buffer.freeze_notify():
|
||||
GLib.idle_add(self._new_line_on_multi_line_markers, *(buffer,))
|
||||
|
||||
return
|
||||
|
||||
has_selection = buffer.get_has_selection()
|
||||
if not has_selection:
|
||||
return self.insert_indent_handler(buffer)
|
||||
|
||||
|
||||
def can_proceed(self, keyname, is_control, is_shift, is_alt):
|
||||
if is_control:
|
||||
if is_shift:
|
||||
if keyname in ["Up", "Down"]:
|
||||
return False
|
||||
|
||||
if keyname in ["w", "t", "o"]:
|
||||
return False
|
||||
|
||||
if is_alt:
|
||||
if keyname in ["Up", "Down", "Left", "Right"]:
|
||||
return False
|
||||
|
||||
return True
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
SourceView Mixins Module
|
||||
"""
|
|
@ -1,157 +0,0 @@
|
|||
# Python imports
|
||||
import random
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gio
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from ..custom_completion_providers.lsp_completion_provider import LSPCompletionProvider
|
||||
|
||||
|
||||
|
||||
class FileEventsMixin:
|
||||
|
||||
def get_current_file(self):
|
||||
return self._current_file
|
||||
|
||||
def open_file(self, gfile, line: int = 0, *args):
|
||||
self._current_file = gfile
|
||||
self._loading_file = True
|
||||
|
||||
self.load_file_info(gfile)
|
||||
self.load_file_async(gfile, line)
|
||||
self._create_file_watcher(gfile)
|
||||
|
||||
def save_file(self):
|
||||
self._skip_file_load = True
|
||||
gfile = event_system.emit_and_await("save_file_dialog", (self._current_filename, self._current_file)) if not self._current_file else self._current_file
|
||||
|
||||
if not gfile:
|
||||
self._skip_file_load = False
|
||||
return
|
||||
|
||||
self.open_file( self._write_file(gfile) )
|
||||
self._skip_file_load = False
|
||||
|
||||
def save_file_as(self):
|
||||
gfile = event_system.emit_and_await("save_file_dialog", (self._current_filename, self._current_file))
|
||||
self._write_file(gfile, True)
|
||||
if gfile: event_system.emit("create_view", (gfile,))
|
||||
|
||||
def load_file_info(self, gfile):
|
||||
info = gfile.query_info("standard::*", 0, cancellable=None)
|
||||
content_type = info.get_content_type()
|
||||
display_name = info.get_display_name()
|
||||
self._current_filename = display_name
|
||||
|
||||
try:
|
||||
lm = self._language_manager.guess_language(None, content_type)
|
||||
self._current_filetype = lm.get_id()
|
||||
self.set_buffer_language(self._current_filetype)
|
||||
except Exception as e:
|
||||
...
|
||||
|
||||
logger.debug(f"Detected Content Type: {content_type}")
|
||||
if self._current_filetype == "buffer":
|
||||
self._current_filetype = info.get_content_type()
|
||||
|
||||
def load_file_async(self, gfile, line: int = 0):
|
||||
if self._skip_file_load:
|
||||
self.update_labels(gfile)
|
||||
return
|
||||
|
||||
file = GtkSource.File()
|
||||
buffer = self.get_buffer()
|
||||
file.set_location(gfile)
|
||||
self._file_loader = GtkSource.FileLoader.new(buffer, file)
|
||||
|
||||
event_system.emit("pause_event_processing")
|
||||
def finish_load_callback(obj, res, user_data = None):
|
||||
event_system.emit("resume_event_processing")
|
||||
|
||||
self._file_loader.load_finish(res)
|
||||
self._document_loaded(line)
|
||||
self.update_labels(gfile)
|
||||
self._loading_file = False
|
||||
|
||||
self._file_loader.load_async(io_priority = 80,
|
||||
cancellable = None,
|
||||
progress_callback = None,
|
||||
progress_callback_data = None,
|
||||
callback = finish_load_callback,
|
||||
user_data = (None))
|
||||
|
||||
def _create_file_watcher(self, gfile = None):
|
||||
if not gfile: return
|
||||
|
||||
self._cancel_current_file_watchers()
|
||||
self._file_change_watcher = gfile.monitor(Gio.FileMonitorFlags.NONE, Gio.Cancellable())
|
||||
self._file_change_watcher.connect("changed", self._file_monitor)
|
||||
|
||||
def _file_monitor(self, file_monitor, file, other_file = None, eve_type = None, data = None):
|
||||
if not file.get_path() == self._current_file.get_path(): return
|
||||
|
||||
if eve_type in [Gio.FileMonitorEvent.CREATED,
|
||||
Gio.FileMonitorEvent.DELETED,
|
||||
Gio.FileMonitorEvent.RENAMED,
|
||||
Gio.FileMonitorEvent.MOVED_IN,
|
||||
Gio.FileMonitorEvent.MOVED_OUT]:
|
||||
buffer = self.get_buffer()
|
||||
buffer.set_modified(True)
|
||||
|
||||
if eve_type in [ Gio.FileMonitorEvent.CHANGES_DONE_HINT ]:
|
||||
if self._ignore_internal_change:
|
||||
self._ignore_internal_change = False
|
||||
return
|
||||
|
||||
# TODO: Any better way to load the difference??
|
||||
if self._current_file.query_exists():
|
||||
self.load_file_async(self._current_file)
|
||||
|
||||
def _cancel_current_file_watchers(self):
|
||||
if self._file_change_watcher:
|
||||
self._file_change_watcher.cancel()
|
||||
self._file_change_watcher = None
|
||||
|
||||
def _write_file(self, gfile, save_as = False):
|
||||
if not gfile: return
|
||||
|
||||
buffer = self.get_buffer()
|
||||
with open(gfile.get_path(), 'w') as f:
|
||||
if not save_as:
|
||||
self._ignore_internal_change = True
|
||||
|
||||
start_itr = buffer.get_start_iter()
|
||||
end_itr = buffer.get_end_iter()
|
||||
text = buffer.get_text(start_itr, end_itr, True)
|
||||
|
||||
f.write(text)
|
||||
|
||||
buffer.set_modified(False)
|
||||
return gfile
|
||||
|
||||
|
||||
def _document_loaded(self, line: int = 0):
|
||||
for provider in self._completion.get_providers():
|
||||
self._completion.remove_provider(provider)
|
||||
|
||||
uri = self._current_file.get_uri()
|
||||
buffer = self.get_buffer()
|
||||
|
||||
event_system.emit("textDocument/didOpen", (self._current_filetype, uri,))
|
||||
|
||||
word_completion = GtkSource.CompletionWords.new("word_completion")
|
||||
word_completion.register(buffer)
|
||||
self._completion.add_provider(word_completion)
|
||||
|
||||
lsp_completion_provider = LSPCompletionProvider(self)
|
||||
self._completion.add_provider(lsp_completion_provider)
|
||||
|
||||
self.got_to_line(buffer, line)
|
||||
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
# Python imports
|
||||
import random
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class MarkEventsMixin:
|
||||
|
||||
def keyboard_insert_mark(self, target_iter = None, is_keyboard_insert = True):
|
||||
buffer = self.get_buffer()
|
||||
|
||||
if not target_iter:
|
||||
target_iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
|
||||
found_mark = self.check_for_insert_marks(target_iter, is_keyboard_insert)
|
||||
if not found_mark:
|
||||
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)
|
||||
self._multi_insert_marks.append(mark)
|
||||
mark.set_visible(True)
|
||||
|
||||
def button_press_insert_mark(self, eve):
|
||||
data = self.window_to_buffer_coords(Gtk.TextWindowType.TEXT , eve.x, eve.y)
|
||||
is_over_text, target_iter, is_trailing = self.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.keyboard_insert_mark(target_iter, is_keyboard_insert = False)
|
||||
|
||||
def check_for_insert_marks(self, target_iter, is_keyboard_insert):
|
||||
marks = target_iter.get_marks()
|
||||
buffer = self.get_buffer()
|
||||
found_mark = False
|
||||
|
||||
for mark in marks:
|
||||
for _mark in self._multi_insert_marks:
|
||||
if _mark == mark:
|
||||
mark.set_visible(False)
|
||||
buffer.delete_mark(mark)
|
||||
found_mark = True
|
||||
break
|
||||
|
||||
if found_mark:
|
||||
self._multi_insert_marks.remove(mark)
|
||||
break
|
||||
|
||||
if not is_keyboard_insert:
|
||||
for mark in marks:
|
||||
if "insert" in mark.get_name():
|
||||
found_mark = True
|
||||
|
||||
return found_mark
|
||||
|
||||
def keyboard_clear_marks(self):
|
||||
buffer = self.get_buffer()
|
||||
|
||||
buffer.begin_user_action()
|
||||
|
||||
for mark in self._multi_insert_marks:
|
||||
mark.set_visible(False)
|
||||
buffer.delete_mark(mark)
|
||||
|
||||
self._multi_insert_marks.clear()
|
||||
buffer.end_user_action()
|
||||
|
||||
|
||||
def _update_multi_line_markers(self, buffer, text_str):
|
||||
for mark in self._multi_insert_marks:
|
||||
iter = buffer.get_iter_at_mark(mark)
|
||||
buffer.insert(iter, text_str, -1)
|
||||
|
||||
self.end_user_action(buffer)
|
||||
|
||||
def _delete_on_multi_line_markers(self, buffer):
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
buffer.backspace(iter, interactive = True, default_editable = True)
|
||||
|
||||
for mark in self._multi_insert_marks:
|
||||
iter = buffer.get_iter_at_mark(mark)
|
||||
buffer.backspace(iter, interactive = True, default_editable = True)
|
||||
|
||||
self.end_user_action(buffer)
|
||||
|
||||
def _new_line_on_multi_line_markers(self, buffer):
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
self._base_indent(buffer, iter)
|
||||
self.insert_indent_handler(buffer, iter)
|
||||
|
||||
for mark in self._multi_insert_marks:
|
||||
iter = buffer.get_iter_at_mark(mark)
|
||||
self._base_indent(buffer, iter)
|
||||
self.insert_indent_handler(buffer, iter)
|
||||
|
||||
def insert_indent_handler(self, buffer, iter = None):
|
||||
if not iter:
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
|
||||
iter_copy = iter.copy()
|
||||
iter_copy.backward_sentence_start()
|
||||
iter_copy.forward_sentence_end()
|
||||
iter_copy.backward_char()
|
||||
|
||||
# Note: Need to compare line nums too because backward_sentence_start
|
||||
# and forward_sentence_end can go farther back if just spaces inbetween...
|
||||
line_start = iter.get_line()
|
||||
line_end = iter_copy.get_line()
|
||||
|
||||
_char = iter_copy.get_char()
|
||||
if _char in ["{", ":"] and (line_start - line_end) == 1:
|
||||
self._indent_deeper(buffer, iter)
|
||||
|
||||
def _base_indent(self, buffer, iter):
|
||||
line_num = iter.get_line()
|
||||
iter_copy = buffer.get_iter_at_line(line_num)
|
||||
|
||||
spaces = ""
|
||||
_char = iter_copy.get_char()
|
||||
while _char == " ":
|
||||
spaces += " "
|
||||
iter_copy.forward_char()
|
||||
_char = iter_copy.get_char()
|
||||
|
||||
buffer.insert(iter, f"\n{spaces}")
|
||||
|
||||
|
||||
def _indent_deeper(self, buffer, iter):
|
||||
buffer.insert(iter, " ")
|
||||
|
||||
|
||||
|
||||
def begin_user_action(self, buffer):
|
||||
if len(self._multi_insert_marks) > 0:
|
||||
buffer.begin_user_action()
|
||||
self.freeze_multi_line_insert = True
|
||||
|
||||
def end_user_action(self, buffer):
|
||||
if len(self._multi_insert_marks) > 0:
|
||||
buffer.end_user_action()
|
||||
self.freeze_multi_line_insert = False
|
|
@ -1,45 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gio
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class SourceViewDnDMixin:
|
||||
|
||||
def _set_up_dnd(self):
|
||||
PLAIN_TEXT_TARGET_TYPE = 70
|
||||
URI_TARGET_TYPE = 80
|
||||
text_target = Gtk.TargetEntry.new('text/plain', Gtk.TargetFlags(0), PLAIN_TEXT_TARGET_TYPE)
|
||||
uri_target = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE)
|
||||
targets = [ text_target, uri_target ]
|
||||
self.drag_dest_set_target_list(targets)
|
||||
|
||||
def _on_drag_data_received(self, widget, drag_context, x, y, data, info, time):
|
||||
if info == 70: return
|
||||
|
||||
if info == 80:
|
||||
buffer = self.get_buffer()
|
||||
uris = data.get_uris()
|
||||
|
||||
if len(uris) == 0:
|
||||
uris = data.get_text().split("\n")
|
||||
|
||||
if not self._current_file and not buffer.get_modified():
|
||||
gfile = Gio.File.new_for_uri(uris[0])
|
||||
self.open_file(gfile)
|
||||
uris.pop(0)
|
||||
|
||||
for uri in uris:
|
||||
gfile = None
|
||||
try:
|
||||
gfile = Gio.File.new_for_uri(uri)
|
||||
except Exception as e:
|
||||
gfile = Gio.File.new_for_path(uri)
|
||||
|
||||
event_system.emit('create_view', (gfile,))
|
|
@ -1,98 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
gi.require_version('GtkSource', '4')
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GLib
|
||||
from gi.repository import Gio
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .source_view_controller import SourceViewControllerMixin
|
||||
|
||||
# from .custom_completion_providers.example_completion_provider import ExampleCompletionProvider
|
||||
# from .custom_completion_providers.python_completion_provider import PythonCompletionProvider
|
||||
|
||||
|
||||
|
||||
class SourceView(SourceViewControllerMixin, GtkSource.View):
|
||||
def __init__(self):
|
||||
super(SourceView, self).__init__()
|
||||
|
||||
self._language_manager = GtkSource.LanguageManager()
|
||||
self._style_scheme_manager = GtkSource.StyleSchemeManager()
|
||||
|
||||
self._file_loader = None
|
||||
self._file_change_watcher = None
|
||||
self._current_file: Gio.File = None
|
||||
|
||||
self._current_filename: str = ""
|
||||
self._current_filepath: str = None
|
||||
self._current_filetype: str = "buffer"
|
||||
|
||||
self._skip_file_load = False
|
||||
self._ignore_internal_change = False
|
||||
self._loading_file = False
|
||||
self._completion = self.get_completion()
|
||||
self._px_value = settings.theming.default_zoom
|
||||
|
||||
self._multi_insert_marks = []
|
||||
self.freeze_multi_line_insert = False
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
ctx = self.get_style_context()
|
||||
ctx.add_class("source-view")
|
||||
ctx.add_class(f"px{self._px_value}")
|
||||
|
||||
self.set_vexpand(True)
|
||||
|
||||
self.set_show_line_marks(True)
|
||||
self.set_show_line_numbers(True)
|
||||
self.set_smart_backspace(True)
|
||||
self.set_indent_on_tab(True)
|
||||
self.set_insert_spaces_instead_of_tabs(True)
|
||||
self.set_auto_indent(True)
|
||||
self.set_monospace(True)
|
||||
self.set_tab_width(4)
|
||||
self.set_show_right_margin(True)
|
||||
self.set_right_margin_position(80)
|
||||
self.set_background_pattern(0) # 0 = None, 1 = Grid
|
||||
|
||||
buffer = self.get_buffer()
|
||||
self._create_default_tag(buffer)
|
||||
self.set_buffer_language(buffer)
|
||||
self.set_buffer_style(buffer)
|
||||
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("focus", self._on_widget_focus)
|
||||
self.connect("focus-in-event", self._focus_in_event)
|
||||
|
||||
self.connect("drag-data-received", self._on_drag_data_received)
|
||||
self.connect("key-press-event", self._key_press_event)
|
||||
self.connect("key-release-event", self._key_release_event)
|
||||
self.connect("button-press-event", self._button_press_event)
|
||||
self.connect("scroll-event", self._scroll_event)
|
||||
|
||||
buffer = self.get_buffer()
|
||||
buffer.connect('changed', self._is_modified)
|
||||
buffer.connect("mark-set", self._on_cursor_move)
|
||||
buffer.connect('insert-text', self._insert_text)
|
||||
buffer.connect('modified-changed', self._buffer_modified_changed)
|
||||
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
self._set_up_dnd()
|
|
@ -1,138 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from .key_input_controller import KeyInputController
|
||||
from .source_view_events import SourceViewEvents
|
||||
|
||||
|
||||
|
||||
class SourceViewControllerMixin(KeyInputController, SourceViewEvents):
|
||||
def get_current_file(self):
|
||||
return self._current_file
|
||||
|
||||
def get_filetype(self):
|
||||
return self._current_filetype
|
||||
|
||||
def set_buffer_language(self, buffer, language = "python3"):
|
||||
buffer.set_language( self._language_manager.get_language(language) )
|
||||
|
||||
def set_buffer_style(self, buffer, style = settings.theming.syntax_theme):
|
||||
buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) )
|
||||
|
||||
def go_to_call(self):
|
||||
buffer = self.get_buffer()
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
line = iter.get_line()
|
||||
offset = iter.get_line_offset()
|
||||
uri = self.get_current_file().get_uri()
|
||||
|
||||
event_system.emit("textDocument/definition", (self.get_filetype(), uri, line, offset,))
|
||||
|
||||
|
||||
def update_cursor_position(self, buffer = None):
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
iter = buffer.get_iter_at_mark( buffer.get_insert() )
|
||||
chars = iter.get_offset()
|
||||
row = iter.get_line() + 1
|
||||
col = self.get_visual_column(iter) + 1
|
||||
|
||||
event_system.emit("set_line_char_label", (f"{row}:{col}",))
|
||||
|
||||
def update_labels(self, gfile = None):
|
||||
if not gfile: return
|
||||
|
||||
tab_widget = self.get_parent().get_tab_widget()
|
||||
tab_widget.set_tab_label(self._current_filename)
|
||||
self.set_bottom_labels(gfile)
|
||||
|
||||
def set_bottom_labels(self, gfile = None):
|
||||
if not gfile: return
|
||||
|
||||
event_system.emit("set_bottom_labels", (gfile, None, self._current_filetype, None))
|
||||
self.update_cursor_position()
|
||||
|
||||
def got_to_line(self, buffer = None, line: int = 0):
|
||||
buffer = self.get_buffer() if not buffer else buffer
|
||||
line_itr = buffer.get_iter_at_line(line)
|
||||
char_iter = buffer.get_iter_at_line_offset(line, line_itr.get_bytes_in_line())
|
||||
|
||||
buffer.place_cursor(char_iter)
|
||||
# Note: scroll_to_iter and scroll_to_mark depend on an idle recalculate of buffers after load to work
|
||||
GLib.idle_add(self.scroll_to_mark, buffer.get_insert(), 0.1, True, 0.0, 0.1)
|
||||
|
||||
def toggle_highlight_line(self, widget = None, eve = None):
|
||||
self.set_highlight_current_line( not self.get_highlight_current_line() )
|
||||
|
||||
def scale_up_text(self, buffer = None, scale_step = 10):
|
||||
if not buffer:
|
||||
buffer = self.get_buffer()
|
||||
|
||||
ctx = self.get_style_context()
|
||||
|
||||
if self._px_value < 99:
|
||||
self._px_value += 1
|
||||
ctx.add_class(f"px{self._px_value}")
|
||||
|
||||
# NOTE: Hope to bring this or similar back after we decouple scaling issues coupled with the miniview.
|
||||
# tag_table = buffer.get_tag_table()
|
||||
# start_itr = buffer.get_start_iter()
|
||||
# end_itr = buffer.get_end_iter()
|
||||
# tag = tag_table.lookup('general_style')
|
||||
#
|
||||
# tag.set_property('scale', tag.get_property('scale') + scale_step)
|
||||
# buffer.apply_tag(tag, start_itr, end_itr)
|
||||
|
||||
def scale_down_text(self, buffer = None, scale_step = 10):
|
||||
if not buffer:
|
||||
buffer = self.get_buffer()
|
||||
|
||||
ctx = self.get_style_context()
|
||||
|
||||
if self._px_value > 1:
|
||||
ctx.remove_class(f"px{self._px_value}")
|
||||
self._px_value -= 1
|
||||
ctx.add_class(f"px{self._px_value}")
|
||||
|
||||
# NOTE: Hope to bring this or similar back after we decouple scaling issues coupled with the miniview.
|
||||
# tag_table = buffer.get_tag_table()
|
||||
# start_itr = buffer.get_start_iter()
|
||||
# end_itr = buffer.get_end_iter()
|
||||
# tag = tag_table.lookup('general_style')
|
||||
#
|
||||
# tag.set_property('scale', tag.get_property('scale') - scale_step)
|
||||
# buffer.apply_tag(tag, start_itr, end_itr)
|
||||
|
||||
def keyboard_undo(self):
|
||||
buffer = self.get_buffer()
|
||||
buffer.undo()
|
||||
|
||||
def keyboard_redo(self):
|
||||
buffer = self.get_buffer()
|
||||
buffer.redo()
|
||||
|
||||
def keyboard_move_lines_up(self):
|
||||
buffer = self.get_buffer()
|
||||
|
||||
self.begin_user_action(buffer)
|
||||
|
||||
self.emit("move-lines", *(False,))
|
||||
# unindent_lines
|
||||
# self.emit("move-words", *(self, 4,))
|
||||
|
||||
self.end_user_action(buffer)
|
||||
|
||||
def keyboard_move_lines_down(self):
|
||||
buffer = self.get_buffer()
|
||||
|
||||
self.begin_user_action(buffer)
|
||||
|
||||
self.emit("move-lines", *(True,))
|
||||
# self.emit("move-words", *(self, -4,))
|
||||
|
||||
self.end_user_action(buffer)
|
|
@ -1,102 +0,0 @@
|
|||
# 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
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from .mixins.source_view_dnd_mixin import SourceViewDnDMixin
|
||||
from .mixins.source_file_events_mixin import FileEventsMixin
|
||||
from .mixins.source_mark_events_mixin import MarkEventsMixin
|
||||
|
||||
|
||||
class SourceViewEvents(SourceViewDnDMixin, MarkEventsMixin, FileEventsMixin):
|
||||
def _create_default_tag(self, buffer):
|
||||
general_style_tag = buffer.create_tag('general_style')
|
||||
general_style_tag.set_property('size', 100)
|
||||
general_style_tag.set_property('scale', 100)
|
||||
|
||||
def _is_modified(self, *args):
|
||||
buffer = self.get_buffer()
|
||||
file_type = self.get_filetype()
|
||||
|
||||
if not self._loading_file:
|
||||
event_system.emit("buffer_changed", (buffer, ))
|
||||
event_system.emit("textDocument/didChange", (file_type, buffer, ))
|
||||
else:
|
||||
event_system.emit("buffer_changed_first_load", (buffer, ))
|
||||
|
||||
self.update_cursor_position(buffer)
|
||||
|
||||
def _insert_text(self, buffer, location_itr, text_str, len_int):
|
||||
if self.freeze_multi_line_insert: return
|
||||
|
||||
self.begin_user_action(buffer)
|
||||
with buffer.freeze_notify():
|
||||
GLib.idle_add(self._update_multi_line_markers, *(buffer, text_str,))
|
||||
|
||||
def _buffer_modified_changed(self, buffer):
|
||||
tab_widget = self.get_parent().get_tab_widget()
|
||||
tab_widget.set_status(changed = True if buffer.get_modified() else False)
|
||||
|
||||
|
||||
def _button_press_event(self, widget = None, eve = None, user_data = None):
|
||||
if eve.type == Gdk.EventType.BUTTON_PRESS and eve.button == 1 : # l-click
|
||||
if eve.state & Gdk.ModifierType.CONTROL_MASK:
|
||||
self.button_press_insert_mark(eve)
|
||||
return True
|
||||
else:
|
||||
self.keyboard_clear_marks()
|
||||
elif eve.type == Gdk.EventType.BUTTON_RELEASE and eve.button == 3: # r-click
|
||||
...
|
||||
|
||||
def _scroll_event(self, widget, eve):
|
||||
accel_mask = Gtk.accelerator_get_default_mod_mask()
|
||||
x, y, z = eve.get_scroll_deltas()
|
||||
if eve.state & accel_mask == Gdk.ModifierType.CONTROL_MASK:
|
||||
buffer = self.get_buffer()
|
||||
if z > 0:
|
||||
self.scale_down_text(buffer)
|
||||
else:
|
||||
self.scale_up_text(buffer)
|
||||
|
||||
return True
|
||||
|
||||
if eve.state & accel_mask == Gdk.ModifierType.SHIFT_MASK:
|
||||
adjustment = self.get_hadjustment()
|
||||
current_val = adjustment.get_value()
|
||||
step_val = adjustment.get_step_increment()
|
||||
|
||||
if z > 0: # NOTE: scroll left
|
||||
adjustment.set_value(current_val - step_val * 2)
|
||||
else: # NOTE: scroll right
|
||||
adjustment.set_value(current_val + step_val * 2)
|
||||
|
||||
return True
|
||||
|
||||
def _focus_in_event(self, widget, eve = None):
|
||||
event_system.emit("set_active_src_view", (self,))
|
||||
self.get_parent().get_parent().is_editor_focused = True
|
||||
|
||||
def _on_widget_focus(self, widget, eve = None):
|
||||
tab_view = self.get_parent().get_parent()
|
||||
path = self._current_file if self._current_file else ""
|
||||
|
||||
event_system.emit('focused_target_changed', (tab_view.NAME,))
|
||||
event_system.emit("set_path_label", (path,))
|
||||
event_system.emit("set_encoding_label")
|
||||
event_system.emit("set_file_type_label", (self._current_filetype,))
|
||||
|
||||
return False
|
||||
|
||||
def _on_cursor_move(self, buffer, cursor_iter, mark, user_data = None):
|
||||
if mark != buffer.get_insert(): return
|
||||
|
||||
self.update_cursor_position(buffer)
|
||||
|
||||
# NOTE: Not sure but this might not be efficient if the map reloads the same view...
|
||||
event_system.emit(f"set_source_view", (self,))
|
|
@ -1,46 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from ..tab_header_widget import TabHeaderWidget
|
||||
from .sourceview.source_view import SourceView
|
||||
|
||||
|
||||
|
||||
class SourceViewContainer(Gtk.ScrolledWindow):
|
||||
def __init__(self, close_tab):
|
||||
super(SourceViewContainer, self).__init__()
|
||||
|
||||
self._close_tab: function = close_tab
|
||||
self._source_view: SourceView = None
|
||||
self._tab_widget: TabHeaderWidget = None
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
...
|
||||
|
||||
def _setup_signals(self):
|
||||
...
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
self._source_view = SourceView()
|
||||
self._tab_widget = TabHeaderWidget(self, self._source_view, self._close_tab)
|
||||
self.add(self._source_view)
|
||||
|
||||
def get_tab_widget(self):
|
||||
return self._tab_widget
|
||||
|
||||
def get_source_view(self):
|
||||
return self._source_view
|
|
@ -49,6 +49,10 @@ class AceEditor(WebKit2.WebView):
|
|||
event_system.subscribe(f"updated_session_{self.INDEX}", self.updated_session)
|
||||
event_system.subscribe(f"close_session_{self.INDEX}", self.close_session)
|
||||
event_system.subscribe(f"remove_session_{self.INDEX}", self.remove_session)
|
||||
event_system.subscribe(f"keyboard_scale_up_text_{self.INDEX}", self.keyboard_scale_up_text)
|
||||
event_system.subscribe(f"keyboard_scale_down_text_{self.INDEX}", self.keyboard_scale_down_text)
|
||||
event_system.subscribe(f"toggle_highlight_line_{self.INDEX}", self.toggle_highlight_line)
|
||||
|
||||
event_system.subscribe(f"ui_message_{self.INDEX}", self.ui_message)
|
||||
|
||||
def _load_settings(self):
|
||||
|
@ -104,6 +108,18 @@ class AceEditor(WebKit2.WebView):
|
|||
command = f"removeSession('{fhash}')"
|
||||
self.run_javascript(command, None, None)
|
||||
|
||||
def keyboard_scale_up_text(self):
|
||||
command = "zoomIn()"
|
||||
self.run_javascript(command, None, None)
|
||||
|
||||
def keyboard_scale_down_text(self):
|
||||
command = "zoomOut()"
|
||||
self.run_javascript(command, None, None)
|
||||
|
||||
def toggle_highlight_line(self):
|
||||
command = "toggleLineHighlight()"
|
||||
self.run_javascript(command, None, None)
|
||||
|
||||
def ui_message(self, message, mtype):
|
||||
command = f"displayMessage('{message}', '{mtype}', '3')"
|
||||
self.run_javascript(command, None, None)
|
||||
|
|
|
@ -7,7 +7,6 @@ from gi.repository import Gtk
|
|||
|
||||
# Application imports
|
||||
from ....controllers.files_controller import FilesController
|
||||
# from ...controls.tab_bar import TabBar
|
||||
from .fixed_box import FixedBox
|
||||
|
||||
|
||||
|
@ -44,5 +43,4 @@ class Editor(Gtk.Box):
|
|||
FilesController(self.INDEX)
|
||||
|
||||
def _load_widgets(self):
|
||||
# self.add( TabBar(self.INDEX) )
|
||||
self.add( FixedBox(self.INDEX) )
|
|
@ -25,6 +25,7 @@ class SaveAsButton(Gtk.Button):
|
|||
self.set_always_show_image(True)
|
||||
self.set_image_position(1) # Left - 0, Right = 1
|
||||
self.set_hexpand(False)
|
||||
self.set_sensitive(False)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("released", self._emit_save_as_eve)
|
||||
|
|
|
@ -20,7 +20,7 @@ class ScaleDownButton(Gtk.Button):
|
|||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_label("Zoom")
|
||||
self.set_label("Zoom Out")
|
||||
self.set_image( Gtk.Image.new_from_icon_name("gtk-zoom-out", 4) )
|
||||
self.set_always_show_image(True)
|
||||
self.set_image_position(1) # Left - 0, Right = 1
|
||||
|
|
|
@ -20,7 +20,7 @@ class ScaleUpButton(Gtk.Button):
|
|||
|
||||
|
||||
def _setup_styling(self):
|
||||
self.set_label("Zoom")
|
||||
self.set_label("Zoom In")
|
||||
self.set_image( Gtk.Image.new_from_icon_name("gtk-zoom-in", 4) )
|
||||
self.set_always_show_image(True)
|
||||
self.set_image_position(1) # Left - 0, Right = 1
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
# Application imports
|
||||
from ..tab_header_widget import TabHeaderWidget
|
||||
|
||||
|
||||
|
||||
class TabBar(Gtk.Notebook):
|
||||
def __init__(self, index):
|
||||
super(TabBar, self).__init__()
|
||||
|
||||
self.INDEX = index
|
||||
self.added_tab = None
|
||||
|
||||
self.set_group_name("editor_widget")
|
||||
|
||||
self._setup_styling()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
self.show_all()
|
||||
|
||||
|
||||
def _setup_styling(self):
|
||||
...
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("switch-page", self._switch_page_update)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
event_system.subscribe(f"add_tab_{self.INDEX}", self.add_tab)
|
||||
event_system.subscribe(f"add_tab_with_name_{self.INDEX}", self.add_tab)
|
||||
event_system.subscribe(f"update_tab_{self.INDEX}", self.update_tab)
|
||||
event_system.subscribe(f"close_tab_{self.INDEX}", self.close_tab)
|
||||
|
||||
def _load_widgets(self):
|
||||
start_box = Gtk.Box()
|
||||
end_box = Gtk.Box()
|
||||
|
||||
add_tab = Gtk.Button(label = "+")
|
||||
add_tab.connect("released", self._button_add_tab)
|
||||
|
||||
end_box.add(add_tab)
|
||||
|
||||
start_box.show_all()
|
||||
end_box.show_all()
|
||||
|
||||
self.set_action_widget(start_box, 0)
|
||||
self.set_action_widget(end_box, 1)
|
||||
|
||||
def _button_add_tab(self, widget):
|
||||
event_system.emit(f"new_session_{self.INDEX}")
|
||||
|
||||
def _button_close_tab(self, widget, container):
|
||||
self._close_tab(widget, container)
|
||||
event_system.emit(f"remove_session_{self.INDEX}", (container.fhash))
|
||||
|
||||
# Note: Need to get parent instead given we pass the close_tab method
|
||||
# from a potentially former notebook.
|
||||
def _close_tab(self, widget, container):
|
||||
notebook = container.get_parent()
|
||||
|
||||
if notebook.get_n_pages() < 2: return
|
||||
|
||||
page_num = notebook.page_num(container)
|
||||
notebook.remove_page(page_num)
|
||||
# container._cancel_current_file_watchers()
|
||||
|
||||
def _switch_page_update(self, notebook, container, page_num):
|
||||
if self.added_tab or self.added_tab == None:
|
||||
self.added_tab = False
|
||||
return
|
||||
|
||||
event_system.emit(f"switch_session_{self.INDEX}", (container.fhash,))
|
||||
|
||||
|
||||
def add_tab(self, fhash, title = "[BAD TITLE]"):
|
||||
container = Gtk.EventBox()
|
||||
header = TabHeaderWidget(container, self._button_close_tab)
|
||||
page_num = self.append_page(container, header)
|
||||
|
||||
container.fhash = fhash
|
||||
self.added_tab = True
|
||||
|
||||
header.label.set_label(title)
|
||||
self.set_tab_detachable(container, True)
|
||||
self.set_tab_reorderable(container, True)
|
||||
|
||||
self.show_all()
|
||||
self.set_current_page(page_num)
|
||||
|
||||
def update_tab(self, fhash, title = "[BAD TITLE]"):
|
||||
for child in self.get_children():
|
||||
if child.fhash == fhash:
|
||||
target = child
|
||||
break
|
||||
|
||||
label = self.get_tab_label(target).get_children()[0]
|
||||
label.set_label(title)
|
||||
|
||||
def close_tab(self, fhash):
|
||||
target = None
|
||||
for child in self.get_children():
|
||||
if child.fhash == fhash:
|
||||
target = child
|
||||
break
|
||||
|
||||
self._close_tab(None, target)
|
|
@ -73,6 +73,7 @@ class ThemeButton(Gtk.Button):
|
|||
self.set_image_position(1) # Left - 0, Right = 1
|
||||
self.set_margin_left(5)
|
||||
# self.set_margin_right(5)
|
||||
self.set_sensitive(False)
|
||||
|
||||
def _setup_signals(self):
|
||||
self.connect("clicked", self._show_popover)
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<pre id="editor"></pre>
|
||||
|
||||
<!-- Buffers modal -->
|
||||
<div class="modal fade" id="buffers-modal" tabindex="-1" role="dialog" data-bs-theme="dark">
|
||||
<div class="modal" id="buffers-modal" tabindex="-1" role="dialog" data-bs-theme="dark">
|
||||
<div class="modal-dialog modal-xl" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
@ -47,7 +47,6 @@
|
|||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-bs-dismiss="modal" class="btn btn-danger btn-sm">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,12 +23,16 @@ window.onerror = function(msg, url, line, col, error) {
|
|||
document.addEventListener("keyup", (eve) => {
|
||||
switch (eve.key) {
|
||||
case "ArrowUp":
|
||||
setLabels();
|
||||
break;
|
||||
case "ArrowDown":
|
||||
setLabels();
|
||||
break;
|
||||
case "ArrowLeft":
|
||||
setLabels();
|
||||
break;
|
||||
case "ArrowRight":
|
||||
setLabels();
|
||||
break;
|
||||
case "Enter":
|
||||
if ( isNotNullOrUndefined(previewSel) ) {
|
||||
|
@ -40,8 +44,10 @@ document.addEventListener("keyup", (eve) => {
|
|||
|
||||
previewSel.dispatchEvent(event);
|
||||
}
|
||||
break
|
||||
case "Control":
|
||||
isControlDown = false;
|
||||
break;
|
||||
case "b":
|
||||
if (isControlDown) {
|
||||
if ( isNotNullOrUndefined(previewSel) ) {
|
||||
|
@ -55,6 +61,7 @@ document.addEventListener("keyup", (eve) => {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
setLabels();
|
||||
break
|
||||
}
|
||||
});
|
||||
|
@ -80,7 +87,18 @@ document.addEventListener("keydown", (eve) => {
|
|||
break;
|
||||
case "Control":
|
||||
isControlDown = true;
|
||||
break;
|
||||
default:
|
||||
break
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// editor.session.selection.on('changeCursor', (eve) => {
|
||||
// let ftype = aceSessions[fhash]["ftype"];
|
||||
// let fpath = aceSessions[fhash]["fpath"];
|
||||
// console.log(eve);
|
||||
|
||||
// // sendMessage("set_labels", ftype, "", fpath, "");
|
||||
// });
|
||||
|
||||
|
|
|
@ -2,9 +2,26 @@ const messenger = (window.webkit) ? window.webkit.messageHandlers : (message) =
|
|||
console.log("Message: " + message);
|
||||
};
|
||||
|
||||
const editorOpts = {
|
||||
printMarginColumn: 80,
|
||||
enableBasicAutocompletion: true,
|
||||
enableInlineAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
highlightActiveLine: true,
|
||||
useSoftTabs: true,
|
||||
tabSize: 4,
|
||||
tooltipFollowsMouse: true,
|
||||
useWrapMode: false,
|
||||
scrollPastEnd: 0.5,
|
||||
mergeUndoDeltas: false
|
||||
}
|
||||
|
||||
let editor = null;
|
||||
let previewEditor = null;
|
||||
let aceSessions = {};
|
||||
let currentSession = null;
|
||||
let previewSel = null;
|
||||
let fontSize = 12;
|
||||
let highlightLine = true;
|
||||
let isControlDown = false;
|
|
@ -45,6 +45,13 @@ const editorCommands = [
|
|||
closeSession(currentSession);
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "toggleLineHighlight",
|
||||
bindKey: {win: "ctrl-h", mac: "ctrl-h"},
|
||||
exec: function(editor) {
|
||||
toggleLineHighlight();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "movelinesUp",
|
||||
bindKey: {win: "ctrl-up", mac: "ctrl-up"},
|
||||
|
@ -59,6 +66,27 @@ const editorCommands = [
|
|||
editor.execCommand("movelinesdown");
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "tgglTopMainMenubar",
|
||||
bindKey: {win: "ctrl-0", mac: "ctrl-0"},
|
||||
exec: function(editor) {
|
||||
sendMessage("tggl_top_main_menubar", "", "", "", "");
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "zoomIn",
|
||||
bindKey: {win: "ctrl-=", mac: "ctrl-="},
|
||||
exec: function(editor) {
|
||||
zoomIn();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "zoomOut",
|
||||
bindKey: {win: "ctrl--", mac: "ctrl--"},
|
||||
exec: function(editor) {
|
||||
zoomOut();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "scrollUp",
|
||||
bindKey: {win: "alt-up", mac: "alt-up"},
|
||||
|
|
|
@ -4,20 +4,7 @@ const loadPreviewEditor = () => {
|
|||
|
||||
previewEditor = ace.edit("preview-editor");
|
||||
// Note: https://github.com/ajaxorg/ace/wiki/Configuring-Ace
|
||||
previewEditor.setOptions({
|
||||
printMarginColumn: 80,
|
||||
enableBasicAutocompletion: true,
|
||||
enableInlineAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
highlightActiveLine: true,
|
||||
useSoftTabs: true,
|
||||
tabSize: 4,
|
||||
tooltipFollowsMouse: true,
|
||||
useWrapMode: false,
|
||||
scrollPastEnd: 0.5,
|
||||
mergeUndoDeltas: false
|
||||
});
|
||||
previewEditor.setOptions(editorOpts);
|
||||
|
||||
// Note: https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
|
||||
previewEditor.commands.addCommands(editorCommands);
|
||||
|
@ -30,33 +17,22 @@ const loadEditor = () => {
|
|||
|
||||
editor = ace.edit("editor");
|
||||
// Note: https://github.com/ajaxorg/ace/wiki/Configuring-Ace
|
||||
editor.setOptions({
|
||||
printMarginColumn: 80,
|
||||
enableBasicAutocompletion: true,
|
||||
enableInlineAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: true,
|
||||
highlightActiveLine: true,
|
||||
useSoftTabs: true,
|
||||
tabSize: 4,
|
||||
tooltipFollowsMouse: true,
|
||||
useWrapMode: false,
|
||||
scrollPastEnd: 0.5,
|
||||
mergeUndoDeltas: false
|
||||
});
|
||||
editor.setOptions(editorOpts);
|
||||
|
||||
// Note: https://github.com/ajaxorg/ace/wiki/Default-Keyboard-Shortcuts
|
||||
editor.commands.addCommands(editorCommands);
|
||||
|
||||
editor.setTheme("ace/theme/one_dark");
|
||||
|
||||
editor.addEventListener("click", (eve) => {
|
||||
setLabels();
|
||||
});
|
||||
}
|
||||
|
||||
const loadInitialSession = () => {
|
||||
newSession(null, editor.getSession());
|
||||
}
|
||||
|
||||
|
||||
|
||||
const newSession = (eve = null, session = null) => {
|
||||
let ftype = "buffer";
|
||||
let fhash = Date.now().toString();
|
||||
|
@ -83,6 +59,8 @@ const setSession = (ftype, fhash, session) => {
|
|||
if (ftype !== "buffer") {
|
||||
editor.session.setMode("ace/mode/" + ftype);
|
||||
}
|
||||
|
||||
setLabels();
|
||||
}
|
||||
|
||||
const updateSession = (fhash, ftype, fname, fpath) => {
|
||||
|
@ -203,3 +181,31 @@ const selectNextPreview = () => {
|
|||
|
||||
selectedElm.click();
|
||||
}
|
||||
|
||||
|
||||
const setLabels = () => {
|
||||
let ftype = aceSessions[currentSession]["ftype"];
|
||||
let fpath = aceSessions[currentSession]["fpath"];
|
||||
let cursor = editor.selection.getCursor();
|
||||
let pos = `${cursor.row + 1}:${cursor.column}`;
|
||||
|
||||
sendMessage("set_info_labels", ftype, "", fpath, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const zoomIn = () => {
|
||||
fontSize += 1;
|
||||
document.getElementById('editor').style.fontSize = `${fontSize}px`;
|
||||
}
|
||||
|
||||
const zoomOut = () => {
|
||||
fontSize -= 1;
|
||||
document.getElementById('editor').style.fontSize = `${fontSize}px`;
|
||||
}
|
||||
|
||||
const toggleLineHighlight = () => {
|
||||
highlightLine = !highlightLine;
|
||||
editor.setHighlightActiveLine(highlightLine);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue