Added code view widget and base command system; updated keybinding system
This commit is contained in:
@@ -36,6 +36,5 @@ class FooterContainer(Gtk.Box):
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
3
src/core/widgets/code/__init__.py
Normal file
3
src/core/widgets/code/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Code Package
|
||||
"""
|
||||
29
src/core/widgets/code/command_system.py
Normal file
29
src/core/widgets/code/command_system.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .commands import *
|
||||
|
||||
|
||||
|
||||
class CommandSystem:
|
||||
def __init__(self):
|
||||
super(CommandSystem, self).__init__()
|
||||
|
||||
self.data: list = ()
|
||||
|
||||
|
||||
def set_data(self, *args, **kwargs):
|
||||
self.data = (args, kwargs)
|
||||
|
||||
def exec(self, command: str):
|
||||
if not command in globals(): return
|
||||
|
||||
# method = getattr(self, command, None)
|
||||
method = globals()[command]
|
||||
args, kwargs = self.data
|
||||
if kwargs:
|
||||
method.execute(*args, kwargs)
|
||||
else:
|
||||
method.execute(*args)
|
||||
12
src/core/widgets/code/commands/__init__.py
Normal file
12
src/core/widgets/code/commands/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Commands Package
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
__all__ = [
|
||||
command.replace(".py", "") for command in os.listdir(
|
||||
os.path.dirname(__file__)
|
||||
) if "__init__" not in command
|
||||
]
|
||||
23
src/core/widgets/code/commands/close_file.py
Normal file
23
src/core/widgets/code/commands/close_file.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("Close File Command")
|
||||
file = editor.files.new()
|
||||
buffer = editor.get_buffer()
|
||||
editor.set_buffer(file.buffer)
|
||||
|
||||
editor.files.remove_file(buffer)
|
||||
19
src/core/widgets/code/commands/line_down.py
Normal file
19
src/core/widgets/code/commands/line_down.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("Line Up Command")
|
||||
editor.emit("move-lines", True)
|
||||
19
src/core/widgets/code/commands/line_up.py
Normal file
19
src/core/widgets/code/commands/line_up.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("Line Up Command")
|
||||
editor.emit("move-lines", False)
|
||||
26
src/core/widgets/code/commands/new_file.py
Normal file
26
src/core/widgets/code/commands/new_file.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("New File Command")
|
||||
file = editor.files.new()
|
||||
language = editor.language_manager \
|
||||
.guess_language("file.txt", None)
|
||||
|
||||
file.buffer.set_language(language)
|
||||
file.buffer.set_style_scheme(editor.syntax_theme)
|
||||
|
||||
editor.set_buffer(file.buffer)
|
||||
36
src/core/widgets/code/commands/open_files.py
Normal file
36
src/core/widgets/code/commands/open_files.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("Open File(s) Command")
|
||||
gfiles = event_system.emit_and_await("open_files")
|
||||
if not gfiles: return
|
||||
|
||||
size = len(gfiles)
|
||||
for i, gfile in enumerate(gfiles):
|
||||
file = editor.files.new()
|
||||
file.set_path(gfile)
|
||||
|
||||
language = editor.language_manager \
|
||||
.guess_language(file.fname, None)
|
||||
file.ftype = language
|
||||
file.buffer.set_language(language)
|
||||
file.buffer.set_style_scheme(editor.syntax_theme)
|
||||
|
||||
if i == (size - 1):
|
||||
editor.set_buffer(file.buffer)
|
||||
|
||||
|
||||
18
src/core/widgets/code/commands/show_completion.py
Normal file
18
src/core/widgets/code/commands/show_completion.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
|
||||
gi.require_version('GtkSource', '4')
|
||||
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
def execute(
|
||||
editor: GtkSource.View = None,
|
||||
buffer: GtkSource.Buffer= None
|
||||
):
|
||||
logger.debug("Show Completion Command")
|
||||
61
src/core/widgets/code/completion_manager.py
Normal file
61
src/core/widgets/code/completion_manager.py
Normal file
@@ -0,0 +1,61 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
from gi.repository import GLib
|
||||
|
||||
# Application imports
|
||||
from .custom_completion_providers.lsp_completion_provider import LSPCompletionProvider
|
||||
|
||||
|
||||
|
||||
class CompletionManager():
|
||||
def __init__(self, completer):
|
||||
super(CompletionManager, self).__init__()
|
||||
|
||||
self._completor = completer
|
||||
self._lsp_provider = LSPCompletionProvider()
|
||||
self._timeout_id = None
|
||||
|
||||
|
||||
def request_completion(self):
|
||||
if self._timeout_id:
|
||||
GLib.source_remove(self._timeout_id)
|
||||
|
||||
self._timeout_id = GLib.timeout_add(
|
||||
1000,
|
||||
self._process_request_completion
|
||||
)
|
||||
|
||||
def _process_request_completion(self):
|
||||
print('hello')
|
||||
|
||||
self._timeout_id = None
|
||||
return False
|
||||
|
||||
def _do_completion(self):
|
||||
if self._completor.get_providers():
|
||||
self._mach_completion()
|
||||
else:
|
||||
self._start_completion()
|
||||
|
||||
def _mach_completion(self):
|
||||
"""
|
||||
Note: Use IF providers were added to completion...
|
||||
"""
|
||||
self._completion.match(
|
||||
self._completion.create_context()
|
||||
)
|
||||
|
||||
def _start_completion(self):
|
||||
"""
|
||||
Note: Use IF NO providers have been added to completion...
|
||||
"""
|
||||
self._completion.start(
|
||||
[
|
||||
self._lsp_provider
|
||||
],
|
||||
self._completion.create_context()
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Custom Completion Providers Module
|
||||
"""
|
||||
@@ -0,0 +1,74 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,117 @@
|
||||
# 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):
|
||||
GObject.Object.__init__(self)
|
||||
|
||||
self._icon_theme = Gtk.IconTheme.get_default()
|
||||
|
||||
self.lsp_data = None
|
||||
|
||||
|
||||
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):
|
||||
return True
|
||||
|
||||
def do_get_priority(self):
|
||||
return 1
|
||||
|
||||
def do_get_activation(self):
|
||||
return GtkSource.CompletionActivation.USER_REQUESTED
|
||||
|
||||
|
||||
def do_populate(self, context, items = []):
|
||||
self.lsp_data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# def do_populate(self, context, items = []):
|
||||
# if hasattr(self._source_view, "completion_items"):
|
||||
# items = self._source_view.completion_items
|
||||
|
||||
# proposals = []
|
||||
# for item in items:
|
||||
# proposals.append( self.create_completion_item(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
|
||||
|
||||
# def create_completion_item(self, item):
|
||||
# comp_item = GtkSource.CompletionItem.new()
|
||||
# keys = item.keys()
|
||||
# comp_item.set_label(item["label"])
|
||||
|
||||
# if "insertText" in keys:
|
||||
# comp_item.set_text(item["insertText"])
|
||||
|
||||
# if "additionalTextEdits" in keys:
|
||||
# comp_item.additionalTextEdits = item["additionalTextEdits"]
|
||||
|
||||
# return comp_item
|
||||
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,108 @@
|
||||
# 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
|
||||
127
src/core/widgets/code/key_mapper.py
Normal file
127
src/core/widgets/code/key_mapper.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# Python imports
|
||||
import copy
|
||||
import json
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class NoKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class CtrlKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class ShiftKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class AltKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class CtrlShiftKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class CtrlAltKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class AltShiftKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
class CtrlShiftAltKeyState:
|
||||
held: dict = {}
|
||||
released: dict = {}
|
||||
|
||||
|
||||
|
||||
class KeyMapper:
|
||||
def __init__(self):
|
||||
super(KeyMapper, self).__init__()
|
||||
|
||||
self.state = NoKeyState
|
||||
self._map = {
|
||||
NoKeyState: NoKeyState(),
|
||||
NoKeyState | CtrlKeyState : CtrlKeyState(),
|
||||
NoKeyState | ShiftKeyState: ShiftKeyState(),
|
||||
NoKeyState | AltKeyState : AltKeyState(),
|
||||
NoKeyState | CtrlKeyState | ShiftKeyState : CtrlShiftKeyState(),
|
||||
NoKeyState | CtrlKeyState | AltKeyState : CtrlAltKeyState(),
|
||||
NoKeyState | AltKeyState | ShiftKeyState : AltShiftKeyState(),
|
||||
NoKeyState | CtrlKeyState | ShiftKeyState | AltKeyState: CtrlShiftAltKeyState(),
|
||||
}
|
||||
|
||||
self.load_map()
|
||||
|
||||
|
||||
def load_map(self):
|
||||
self.states = copy.deepcopy(self._map)
|
||||
bindings_file = f"{settings_manager.get_home_config_path()}/code-key-bindings.json"
|
||||
|
||||
with open(bindings_file, 'r') as f:
|
||||
data = json.load(f)["keybindings"]
|
||||
|
||||
for command in data:
|
||||
press_state = "held" if "held" in data[command] else "released"
|
||||
keyname = data[command][press_state]
|
||||
|
||||
state = NoKeyState
|
||||
if "<Control>" in keyname:
|
||||
state = state | CtrlKeyState
|
||||
if "<Shift>" in keyname:
|
||||
state = state | ShiftKeyState
|
||||
if "<Alt>" in keyname:
|
||||
state = state | AltKeyState
|
||||
|
||||
keyname = keyname.replace("<Control>", "") \
|
||||
.replace("<Shift>", "") \
|
||||
.replace("<Alt>", "")
|
||||
|
||||
getattr(self.states[state], press_state)[keyname] = command
|
||||
|
||||
def re_map(self):
|
||||
self.states = copy.deepcopy(self._map)
|
||||
|
||||
def _key_press_event(self, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
print(keyname)
|
||||
|
||||
self._set_key_state(eve)
|
||||
if keyname in self.states[self.state].held:
|
||||
return self.states[self.state].held[keyname]
|
||||
|
||||
def _key_release_event(self, eve):
|
||||
keyname = Gdk.keyval_name(eve.keyval)
|
||||
|
||||
self._set_key_state(eve)
|
||||
if keyname in self.states[self.state].released:
|
||||
return self.states[self.state].released[keyname]
|
||||
|
||||
def _set_key_state(self, eve):
|
||||
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
|
||||
|
||||
self.state = NoKeyState
|
||||
if is_control:
|
||||
self.state = self.state | CtrlKeyState
|
||||
if is_shift:
|
||||
self.state = self.state | ShiftKeyState
|
||||
if is_alt:
|
||||
self.state = self.state | AltKeyState
|
||||
|
||||
40
src/core/widgets/code/source_buffer.py
Normal file
40
src/core/widgets/code/source_buffer.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
import gi
|
||||
gi.require_version('GtkSource', '4')
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
|
||||
|
||||
|
||||
class SourceBuffer(GtkSource.Buffer):
|
||||
def __init__(self):
|
||||
super(SourceBuffer, self).__init__()
|
||||
|
||||
self._handler_ids = []
|
||||
|
||||
|
||||
def set_signals(
|
||||
self,
|
||||
_changed,
|
||||
_mark_set,
|
||||
_insert_text,
|
||||
_modified_changed,
|
||||
):
|
||||
|
||||
self._handler_ids = [
|
||||
self.connect("changed", _changed),
|
||||
self.connect("mark-set", _mark_set),
|
||||
self.connect("insert-text", _insert_text),
|
||||
self.connect("modified-changed", _modified_changed)
|
||||
]
|
||||
|
||||
def clear_signals(self):
|
||||
for handle_id in self._handler_ids:
|
||||
self.disconnect(handle_id)
|
||||
|
||||
def __del__(self):
|
||||
for handle_id in self._handler_ids:
|
||||
self.disconnect(handle_id)
|
||||
77
src/core/widgets/code/source_file.py
Normal file
77
src/core/widgets/code/source_file.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Python imports
|
||||
import os
|
||||
|
||||
# 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 Gio
|
||||
|
||||
# Application imports
|
||||
from .source_buffer import SourceBuffer
|
||||
|
||||
|
||||
|
||||
class SourceFile(GtkSource.File):
|
||||
def __init__(self):
|
||||
super(SourceFile, self).__init__()
|
||||
|
||||
self.encoding: str = "UTF-8"
|
||||
self.fname: str = "buffer"
|
||||
self.fpath: str = "buffer"
|
||||
self.ftype: str = "buffer"
|
||||
|
||||
self.buffer: SourceBuffer = SourceBuffer()
|
||||
|
||||
self._set_signals()
|
||||
|
||||
|
||||
def set_path(self, gfile: Gio.File.new_for_path):
|
||||
if not gfile: return
|
||||
|
||||
self.set_location(gfile)
|
||||
|
||||
self.fpath = gfile.get_parent().get_path(),
|
||||
self.fname = gfile.get_basename()
|
||||
data = gfile.load_bytes()[0].get_data().decode("UTF-8")
|
||||
|
||||
self.buffer.insert_at_cursor(data)
|
||||
|
||||
def _set_signals(self):
|
||||
self.buffer.set_signals(
|
||||
self._changed,
|
||||
self._mark_set,
|
||||
self._insert_text,
|
||||
self._modified_changed
|
||||
)
|
||||
|
||||
def _insert_text(
|
||||
self,
|
||||
buffer: SourceBuffer,
|
||||
location: Gtk.TextIter,
|
||||
text: str,
|
||||
length: int
|
||||
):
|
||||
logger.info("SourceFile._insert_text")
|
||||
|
||||
def _changed(self, buffer: SourceBuffer):
|
||||
logger.info("SourceFile._changed")
|
||||
|
||||
def _mark_set(
|
||||
self,
|
||||
buffer: SourceBuffer,
|
||||
location: Gtk.TextIter,
|
||||
mark: Gtk.TextMark
|
||||
):
|
||||
# logger.info("SourceFile._mark_set")
|
||||
...
|
||||
|
||||
def _modified_changed(self,buffer: SourceBuffer):
|
||||
logger.info("SourceFile._modified_changed")
|
||||
|
||||
def close(self):
|
||||
del self.buffer
|
||||
36
src/core/widgets/code/source_files_manager.py
Normal file
36
src/core/widgets/code/source_files_manager.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# Python imports
|
||||
|
||||
# Lib imports
|
||||
|
||||
# Application imports
|
||||
from .source_file import SourceFile
|
||||
from .source_buffer import SourceBuffer
|
||||
|
||||
|
||||
|
||||
class SourceFilesManager(list):
|
||||
def __init__(self):
|
||||
super(SourceFilesManager, self).__init__()
|
||||
|
||||
|
||||
def new(self):
|
||||
file = SourceFile()
|
||||
super().append(file)
|
||||
|
||||
return file
|
||||
|
||||
def append(self, file: SourceFile):
|
||||
if not file: return
|
||||
|
||||
super().append(file)
|
||||
|
||||
def remove_file(self, buffer: SourceBuffer):
|
||||
if not buffer: return
|
||||
|
||||
for file in self:
|
||||
if not buffer == file.buffer: continue
|
||||
self.remove(file)
|
||||
|
||||
file.close()
|
||||
del file
|
||||
break
|
||||
115
src/core/widgets/code/view.py
Normal file
115
src/core/widgets/code/view.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# 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 GLib
|
||||
from gi.repository import GtkSource
|
||||
|
||||
# Application imports
|
||||
from .source_files_manager import SourceFilesManager
|
||||
from .completion_manager import CompletionManager
|
||||
from .command_system import CommandSystem
|
||||
from .key_mapper import KeyMapper
|
||||
|
||||
|
||||
class SourceView(GtkSource.View):
|
||||
def __init__(self):
|
||||
super(SourceView, self).__init__()
|
||||
|
||||
self.key_mapper = KeyMapper()
|
||||
|
||||
self._setup_styles()
|
||||
self._setup_signals()
|
||||
self._subscribe_to_events()
|
||||
self._load_widgets()
|
||||
|
||||
|
||||
def _setup_styles(self):
|
||||
ctx = self.get_style_context()
|
||||
ctx.add_class("source-view")
|
||||
|
||||
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
|
||||
self.set_highlight_current_line(True)
|
||||
|
||||
def _setup_signals(self):
|
||||
# self.connect("show-completion", self._show_completion)
|
||||
self.map_id = self.connect("map", self._init_map)
|
||||
|
||||
# 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("button-release-event", self._button_release_event)
|
||||
# self.connect("scroll-event", self._scroll_event)
|
||||
|
||||
def _subscribe_to_events(self):
|
||||
...
|
||||
|
||||
def _load_widgets(self):
|
||||
...
|
||||
|
||||
def _init_map(self, view):
|
||||
def _first_show_init():
|
||||
self.disconnect(self.map_id)
|
||||
del self.map_id
|
||||
|
||||
self._handle_first_show()
|
||||
|
||||
return False
|
||||
|
||||
# GLib.timeout_add(1000, _first_show_init)
|
||||
GLib.idle_add(_first_show_init)
|
||||
|
||||
def _handle_first_show(self):
|
||||
self.language_manager = GtkSource.LanguageManager()
|
||||
self.style_scheme_manager = GtkSource.StyleSchemeManager()
|
||||
self.command = CommandSystem()
|
||||
self.files = SourceFilesManager()
|
||||
self.completion = CompletionManager(
|
||||
self.get_completion()
|
||||
)
|
||||
|
||||
self.style_scheme_manager.append_search_path(f"{settings_manager.get_home_config_path()}/code_styles")
|
||||
self.syntax_theme = self.style_scheme_manager.get_scheme("penguins-in-space")
|
||||
|
||||
self.command.set_data(self, self.get_buffer())
|
||||
self.exec_command("new_file")
|
||||
|
||||
def _key_press_event(self, view, eve):
|
||||
command = self.key_mapper._key_press_event(eve)
|
||||
if not command: return False
|
||||
|
||||
self.exec_command(command)
|
||||
return True
|
||||
|
||||
def _key_release_event(self, view, eve):
|
||||
command = self.key_mapper._key_release_event(eve)
|
||||
if not command: return False
|
||||
|
||||
self.exec_command(command)
|
||||
return True
|
||||
|
||||
def _show_completion(self, view):
|
||||
self.completion.request_completion()
|
||||
|
||||
def exec_command(self, command: str):
|
||||
self.command.exec(command)
|
||||
@@ -118,9 +118,8 @@ class Window(Gtk.ApplicationWindow):
|
||||
|
||||
# bind css file
|
||||
cssProvider = Gtk.CssProvider()
|
||||
cssProvider.load_from_path( settings_manager.get_css_file() )
|
||||
screen = Gdk.Screen.get_default()
|
||||
styleContext = Gtk.StyleContext()
|
||||
cssProvider.load_from_path( settings_manager.get_css_file() )
|
||||
styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
|
||||
|
||||
def _area_draw(self, widget: Gtk.ApplicationWindow, cr: cairo.Context) -> None:
|
||||
|
||||
Reference in New Issue
Block a user