Skip to content
Snippets Groups Projects
Commit 4c2076b7 authored by Allen, Bruce (CIV)'s avatar Allen, Bruce (CIV)
Browse files

make mp code highlighting more intuitive

parent a116c2a5
No related branches found
No related tags found
No related merge requests found
from PySide6.QtWidgets import QComboBox
from PySide6.QtCore import Slot, Qt
from PySide6.QtGui import QKeyEvent
class FindAndReplaceCB(QComboBox):
"""A QComboBox that signals when the user presses Enter."""
def __init__(self, parent):
super().__init__(parent)
def set_enter_listener_function(self, enter_listener_function):
self.enter_listener_function = enter_listener_function
@Slot(QKeyEvent)
def keyPressEvent(self, key_event):
if key_event.key() == Qt.Key_Enter or key_event.key() == Qt.Key_Return:
self.enter_listener_function()
super().keyPressEvent(key_event)
......@@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'find_and_replace_dialog.ui'
##
## Created by: Qt User Interface Compiler version 6.2.3
## Created by: Qt User Interface Compiler version 6.3.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
......@@ -15,18 +15,20 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QDialog,
QFormLayout, QLabel, QLineEdit, QPushButton,
QSizePolicy, QWidget)
from PySide6.QtWidgets import (QApplication, QDialog, QFormLayout, QLabel,
QLineEdit, QPushButton, QSizePolicy, QToolButton,
QWidget)
from find_and_replace_cb import FindAndReplaceCB
class Ui_FindAndReplaceDialog(object):
def setupUi(self, FindAndReplaceDialog):
if not FindAndReplaceDialog.objectName():
FindAndReplaceDialog.setObjectName(u"FindAndReplaceDialog")
FindAndReplaceDialog.resize(411, 164)
FindAndReplaceDialog.resize(472, 126)
self.formLayoutWidget = QWidget(FindAndReplaceDialog)
self.formLayoutWidget.setObjectName(u"formLayoutWidget")
self.formLayoutWidget.setGeometry(QRect(10, 20, 391, 61))
self.formLayoutWidget.setGeometry(QRect(10, 20, 391, 63))
self.formLayout = QFormLayout(self.formLayoutWidget)
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setContentsMargins(0, 0, 0, 0)
......@@ -45,32 +47,26 @@ class Ui_FindAndReplaceDialog(object):
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.replace_with_le)
self.find_cb = QComboBox(self.formLayoutWidget)
self.find_cb = FindAndReplaceCB(self.formLayoutWidget)
self.find_cb.setObjectName(u"find_cb")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.find_cb)
self.replace_pb = QPushButton(FindAndReplaceDialog)
self.replace_pb.setObjectName(u"replace_pb")
self.replace_pb.setGeometry(QRect(10, 130, 83, 25))
self.previous_pb = QPushButton(FindAndReplaceDialog)
self.previous_pb.setObjectName(u"previous_pb")
self.previous_pb.setGeometry(QRect(100, 130, 83, 25))
self.next_pb = QPushButton(FindAndReplaceDialog)
self.next_pb.setObjectName(u"next_pb")
self.next_pb.setGeometry(QRect(190, 130, 83, 25))
self.replace_pb.setGeometry(QRect(200, 90, 83, 25))
self.close_pb = QPushButton(FindAndReplaceDialog)
self.close_pb.setObjectName(u"close_pb")
self.close_pb.setGeometry(QRect(320, 130, 83, 25))
self.match_case_cb = QCheckBox(FindAndReplaceDialog)
self.match_case_cb.setObjectName(u"match_case_cb")
self.match_case_cb.setGeometry(QRect(10, 90, 111, 23))
self.close_pb.setGeometry(QRect(380, 90, 83, 25))
self.previous_tb = QToolButton(FindAndReplaceDialog)
self.previous_tb.setObjectName(u"previous_tb")
self.previous_tb.setGeometry(QRect(410, 20, 21, 26))
self.next_tb = QToolButton(FindAndReplaceDialog)
self.next_tb.setObjectName(u"next_tb")
self.next_tb.setGeometry(QRect(440, 20, 21, 26))
QWidget.setTabOrder(self.find_cb, self.replace_with_le)
QWidget.setTabOrder(self.replace_with_le, self.match_case_cb)
QWidget.setTabOrder(self.match_case_cb, self.replace_pb)
QWidget.setTabOrder(self.replace_pb, self.previous_pb)
QWidget.setTabOrder(self.previous_pb, self.next_pb)
QWidget.setTabOrder(self.next_pb, self.close_pb)
QWidget.setTabOrder(self.replace_with_le, self.replace_pb)
QWidget.setTabOrder(self.replace_pb, self.close_pb)
self.retranslateUi(FindAndReplaceDialog)
......@@ -82,9 +78,8 @@ class Ui_FindAndReplaceDialog(object):
self.find_l.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Find", None))
self.replace_l.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Replace with", None))
self.replace_pb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Replace", None))
self.previous_pb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Previous", None))
self.next_pb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Next", None))
self.close_pb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Close", None))
self.match_case_cb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"Match case", None))
self.previous_tb.setText(QCoreApplication.translate("FindAndReplaceDialog", u"<", None))
self.next_tb.setText(QCoreApplication.translate("FindAndReplaceDialog", u">", None))
# retranslateUi
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>411</width>
<height>164</height>
<width>472</width>
<height>126</height>
</rect>
</property>
<property name="windowTitle">
......@@ -19,7 +19,7 @@
<x>10</x>
<y>20</y>
<width>391</width>
<height>61</height>
<height>63</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
......@@ -41,15 +41,15 @@
<widget class="QLineEdit" name="replace_with_le"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="find_cb"/>
<widget class="FindAndReplaceCB" name="find_cb"/>
</item>
</layout>
</widget>
<widget class="QPushButton" name="replace_pb">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<x>200</x>
<y>90</y>
<width>83</width>
<height>25</height>
</rect>
......@@ -58,66 +58,57 @@
<string>Replace</string>
</property>
</widget>
<widget class="QPushButton" name="previous_pb">
<property name="geometry">
<rect>
<x>100</x>
<y>130</y>
<width>83</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Previous</string>
</property>
</widget>
<widget class="QPushButton" name="next_pb">
<widget class="QPushButton" name="close_pb">
<property name="geometry">
<rect>
<x>190</x>
<y>130</y>
<x>380</x>
<y>90</y>
<width>83</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Next</string>
<string>Close</string>
</property>
</widget>
<widget class="QPushButton" name="close_pb">
<widget class="QToolButton" name="previous_tb">
<property name="geometry">
<rect>
<x>320</x>
<y>130</y>
<width>83</width>
<height>25</height>
<x>410</x>
<y>20</y>
<width>21</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Close</string>
<string>&lt;</string>
</property>
</widget>
<widget class="QCheckBox" name="match_case_cb">
<widget class="QToolButton" name="next_tb">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>111</width>
<height>23</height>
<x>440</x>
<y>20</y>
<width>21</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Match case</string>
<string>&gt;</string>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>FindAndReplaceCB</class>
<extends>QComboBox</extends>
<header>find_and_replace_cb</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>find_cb</tabstop>
<tabstop>replace_with_le</tabstop>
<tabstop>match_case_cb</tabstop>
<tabstop>replace_pb</tabstop>
<tabstop>previous_pb</tabstop>
<tabstop>next_pb</tabstop>
<tabstop>close_pb</tabstop>
</tabstops>
<resources/>
......
......@@ -12,14 +12,6 @@ from find_and_replace_dialog import Ui_FindAndReplaceDialog
from paths_gryphon import SNAPSHOTS_PATH
from mp_code_dynamic_tokens import dynamic_token_names
def _find_flags(is_backward, is_match_case):
options = QTextDocument.FindFlags()
if is_backward:
options |= QTextDocument.FindBackward
if is_match_case:
options |= QTextDocument.FindCaseSensitively
return options
class FindAndReplaceDialogWrapper(QDialog):
# use this singleton class variable to enable action in code view menu
......@@ -40,8 +32,9 @@ class FindAndReplaceDialogWrapper(QDialog):
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
self.ui.find_cb.setEditable(True)
self.ui.find_cb.setInsertPolicy(QComboBox.InsertPolicy.NoInsert)
self.ui.find_cb.completer().setCaseSensitivity(
Qt.CaseSensitivity.CaseSensitive)
# connect find input to the _next_clicked function
self.ui.find_cb.set_enter_listener_function(self._next_clicked)
# connect to know view change
mp_code_view.cursorPositionChanged.connect(self._maybe_set_ui_state)
......@@ -52,11 +45,9 @@ class FindAndReplaceDialogWrapper(QDialog):
self._set_ui_state_ui_text_changed)
self.ui.replace_with_le.textEdited.connect(
self._set_ui_state_ui_text_changed)
self.ui.match_case_cb.stateChanged.connect(
self._set_ui_state)
self.ui.replace_pb.clicked.connect(self._replace_clicked)
self.ui.previous_pb.clicked.connect(self._previous_clicked)
self.ui.next_pb.clicked.connect(self._next_clicked)
self.ui.previous_tb.clicked.connect(self._previous_clicked)
self.ui.next_tb.clicked.connect(self._next_clicked)
self.ui.close_pb.clicked.connect(self._close_clicked)
# start empty
......@@ -112,17 +103,21 @@ class FindAndReplaceDialogWrapper(QDialog):
# previous button
cursor = self.mp_code_view.textCursor()
options = _find_flags(True, self.ui.match_case_cb.isChecked())
has_match_backward = text_to_find and self.document.find(text_to_find,
cursor, options) != QTextCursor()
self.ui.previous_pb.setDisabled(not has_match_backward)
cursor, QTextDocument.FindBackward) != QTextCursor()
self.ui.previous_tb.setDisabled(not self._has_match())
# next button
cursor = self.mp_code_view.textCursor()
options = _find_flags(False, self.ui.match_case_cb.isChecked())
has_match_forward = text_to_find and self.document.find(text_to_find,
cursor, options) != QTextCursor()
self.ui.next_pb.setDisabled(not has_match_forward)
cursor)
self.ui.next_tb.setDisabled(not self._has_match())
def _has_match(self):
text_to_find = self.ui.find_cb.currentText()
next_cursor = self.mp_code_view.mp_code_manager.document.find(
text_to_find)
return bool(next_cursor)
@Slot(str)
def _set_ui_state_ui_text_changed(self, _text):
......@@ -148,26 +143,42 @@ class FindAndReplaceDialogWrapper(QDialog):
@Slot()
def _previous_clicked(self):
text_to_find = self.ui.find_cb.currentText()
options = _find_flags(True, self.ui.match_case_cb.isChecked())
success = self.mp_code_view.find(text_to_find, options)
success = self.mp_code_view.find(text_to_find,
QTextDocument.FindBackward)
if not success:
print("Error in find_and_replace _previous_clicked")
raise RuntimeError("bad")
# maybe start over from the end
cursor = QTextCursor(self.mp_code_view.mp_code_manager.document)
cursor.movePosition(QTextCursor.End)
previous_cursor = self.mp_code_view.mp_code_manager.document.find(
text_to_find, cursor, QTextDocument.FindBackward)
if previous_cursor:
self.mp_code_view.setTextCursor(previous_cursor)
else:
print("Error in find_and_replace previous: "
" '%s' not found"%text_to_find)
# next
@Slot()
def _next_clicked(self):
text_to_find = self.ui.find_cb.currentText()
options = _find_flags(False, self.ui.match_case_cb.isChecked())
success = self.mp_code_view.find(text_to_find, options)
success = self.mp_code_view.find(text_to_find)
if not success:
print("Error in find_and_replace _next_clicked")
raise RuntimeError("bad")
# maybe start over from the beginning
next_cursor = self.mp_code_view.mp_code_manager.document.find(
text_to_find)
if next_cursor:
self.mp_code_view.setTextCursor(next_cursor)
else:
print("Error in find_and_replace next: "
" '%s' not found"%text_to_find)
# close
@Slot()
def _close_clicked(self):
FindAndReplaceDialogWrapper.a_wrapper_instance_is_visible = False
self.ui.find_cb.clearEditText()
self.hide()
......@@ -7,7 +7,6 @@ from PySide6.QtCore import QSize
from PySide6.QtGui import QTextOption, QFont, QPainter
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QDialog, QAbstractItemView, QPlainTextEdit
from mp_code_highlighter import MPCodeHighlighter
from model_statistics_dialog import Ui_ModelStatisticsDialog
from model_statistics import model_statistics
......
......@@ -21,13 +21,16 @@ class MPCodeCursorHighlighter(QObject):
self.document = mp_code_view.mp_code_manager.document
# background color
self.word_background_color = QColor("#e4ffe8")
self.mate_background_color = self.word_background_color.darker(110)
self.selected_background_color = QColor("#ffff00") # yellow
self.find_background_color = self.selected_background_color.darker(108)
self.mate_background_color = QColor("#e4ffe8")
self.mateless_background_color = QColor("#ffd6cc")
# connect
mp_code_view.cursorPositionChanged.connect(self.highlight_extra)
mp_code_view.textChanged.connect(self.highlight_extra)
mp_code_view.find_and_replace_dialog_wrapper.ui.find_cb \
.currentTextChanged.connect(self.highlight_extra_text)
# any selected word
self.selected_word = ""
......@@ -35,24 +38,10 @@ class MPCodeCursorHighlighter(QObject):
# the QTextEdit.ExtraSelections objects to highlight
self.extra_selections = list()
# add pair type ExtraSelection
def _add_mate_selection(self, cursor):
# add cursor content to extra selection
def _add_selection(self, cursor, color):
extra_selection = QTextEdit.ExtraSelection()
extra_selection.format.setBackground(self.mate_background_color)
extra_selection.cursor = QTextCursor(cursor)
self.extra_selections.append(extra_selection)
# add mateless pair type ExtraSelection
def _add_mateless_selection(self, cursor):
extra_selection = QTextEdit.ExtraSelection()
extra_selection.format.setBackground(self.mateless_background_color)
extra_selection.cursor = QTextCursor(cursor)
self.extra_selections.append(extra_selection)
# add word type ExtraSelection
def _add_word_selection(self, cursor):
extra_selection = QTextEdit.ExtraSelection()
extra_selection.format.setBackground(self.word_background_color)
extra_selection.format.setBackground(color)
extra_selection.cursor = QTextCursor(cursor)
self.extra_selections.append(extra_selection)
......@@ -75,7 +64,7 @@ class MPCodeCursorHighlighter(QObject):
raise Exception("Bad")
# highlight open token
self._add_mate_selection(cursor)
self._add_selection(cursor, self.mate_background_color)
# remember open token cursor in case mate cannot be found
open_token_cursor = QTextCursor(cursor)
......@@ -93,7 +82,8 @@ class MPCodeCursorHighlighter(QObject):
if not cursor.selectedText():
# None so open token has no mate
self._add_mateless_selection(open_token_cursor)
self._add_selection(open_token_cursor,
self.mateless_background_color)
break
if cursor.selectedText() == open_token:
count += 1
......@@ -102,7 +92,7 @@ class MPCodeCursorHighlighter(QObject):
count -= 1
if count == 0:
# at mate so add highlight
self._add_mate_selection(cursor)
self._add_selection(cursor, self.mate_background_color)
break
# highlight mate going backward, cursor captures close_token
......@@ -124,7 +114,7 @@ class MPCodeCursorHighlighter(QObject):
raise Exception("Bad")
# highlight close token
self._add_mate_selection(cursor)
self._add_selection(cursor, self.mate_background_color)
# remember open token cursor in case mate cannot be found
close_token_cursor = QTextCursor(cursor)
......@@ -146,7 +136,8 @@ class MPCodeCursorHighlighter(QObject):
if not cursor.selectedText():
# None so close token has no mate
self._add_mateless_selection(close_token_cursor)
self._add_selection(close_token_cursor,
self.mateless_background_color)
break
if cursor.selectedText() == close_token:
count += 1
......@@ -155,7 +146,7 @@ class MPCodeCursorHighlighter(QObject):
count -= 1
if count == 0:
# at mate so add highlight
self._add_mate_selection(cursor)
self._add_selection(cursor, self.mate_background_color)
break
# highlight pair
......@@ -226,47 +217,46 @@ class MPCodeCursorHighlighter(QObject):
# not an IF or DO block
pass
def _highlight_extra_text(self, text, color):
# scan document for matches
selected_text_expression = QRegularExpression(text)
cursor = QTextCursor()
cursor.movePosition(QTextCursor.MoveOperation.Start)
while True:
cursor = self.document.find(selected_text_expression, cursor)
if not cursor.selectedText():
break
self._add_selection(cursor, color)
# highlight open token, close token pair and anything matching selected_word
@Slot()
def highlight_extra(self):
self.extra_selections.clear()
# highlight any selected word
if self.selected_word:
word_expression = QRegularExpression("\\b%s\\b"%self.selected_word)
# find word starting at the beginning
cursor = QTextCursor()
cursor.movePosition(QTextCursor.MoveOperation.Start)
while True:
cursor = self.document.find(word_expression, cursor)
if not cursor.selectedText():
break
self._add_word_selection(cursor)
# highlight any pair if cursor is on a pair side
# highlight any selected text
selected_text = self.mp_code_view.textCursor().selectedText()
if selected_text:
self._highlight_extra_text(selected_text,
self.selected_background_color)
# highlight any find text for find-and-replace
if self.mp_code_view.find_and_replace_dialog_wrapper.isVisible():
find_text = self.mp_code_view.find_and_replace_dialog_wrapper \
.ui.find_cb.currentText()
if find_text:
self._highlight_extra_text(find_text,
self.find_background_color)
# highlight any pair if cursor is over a pairable token
self._highlight_pair()
# now apply the extra selections
# now put these extra selections into the view
self.mp_code_view.setExtraSelections(self.extra_selections)
# arrive here from mouse press from text edit
def highlight_selection(self):
# get word
cursor = self.mp_code_view.textCursor()
cursor.movePosition(QTextCursor.MoveOperation.StartOfWord)
cursor.movePosition(QTextCursor.MoveOperation.EndOfWord,
QTextCursor.MoveMode.KeepAnchor)
self.selected_word = cursor.selectedText()
# disallow word if it starts with potential regex control codes
if self.selected_word and self.selected_word[0] != "_" and \
not self.selected_word[0].isalnum():
self.selected_word = ""
# now that the selected word is defined call higlight extra
@Slot(str)
def highlight_extra_text(self, _text):
self.highlight_extra()
......@@ -2,7 +2,7 @@ from PySide6.QtGui import QTextDocument
from PySide6.QtCore import Signal, Slot
from PySide6.QtCore import QObject
from PySide6.QtWidgets import QPlainTextDocumentLayout
from mp_code_highlighter import MPCodeHighlighter
from mp_code_syntax_highlighter import MPCodeSyntaxHighlighter
from mp_code_syntax_checker import mp_check_syntax
# use this to signal completion to GUI.
......@@ -59,7 +59,7 @@ class MPCodeManager(QObject):
self.statusbar = statusbar
self.document = QTextDocument()
self.document.setDocumentLayout(QPlainTextDocumentLayout(self.document))
self.highlighter = MPCodeHighlighter(self.document,
self.highlighter = MPCodeSyntaxHighlighter(self.document,
signal_settings_changed)
self.syntax_error_line_number = 0
......
......@@ -22,7 +22,7 @@ def _text_char_format(color, style=''):
return text_char_format
class MPCodeHighlighter(QSyntaxHighlighter):
class MPCodeSyntaxHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for MP Code.
Manages MP Code syntax highlighting.
......@@ -32,7 +32,7 @@ class MPCodeHighlighter(QSyntaxHighlighter):
"""
def __init__(self, document, signal_settings_changed):
super(MPCodeHighlighter, self).__init__(document)
super(MPCodeSyntaxHighlighter, self).__init__(document)
self.document = document
self.skip_recalculate_names = False
......@@ -45,7 +45,7 @@ class MPCodeHighlighter(QSyntaxHighlighter):
# handle document change event
document.contentsChanged.connect(self.recalculate_names)
# handle settings change event to get changed colors
# handle settings change event to reset colors
signal_settings_changed.connect(self.set_colors)
# set colors
......
......@@ -104,11 +104,11 @@ class MPCodeView(QPlainTextEdit):
self.action_save_selection.triggered.connect(self._save_selection)
# the cursor highlighter manages cursor highlighting on mouse click
# the cursor highlighter manages cursor-based highlighting
self.mp_code_cursor_highlighter = MPCodeCursorHighlighter(self)
# the word list model provides the list model for MP Code keywords
self.mp_code_string_list_model = QStringListModel()
self.mp_code_word_list_model = QStringListModel()
self.blockCountChanged.connect(self.update_line_number_area_width)
self.updateRequest.connect(self.update_line_number_area)
......@@ -136,7 +136,7 @@ class MPCodeView(QPlainTextEdit):
self.syntax_error_color = QColor("#cc0000")
# code completer
self.completer = QCompleter(self.mp_code_string_list_model, None)
self.completer = QCompleter(self.mp_code_word_list_model, None)
self.completer.setWidget(self)
self.completer.setCompletionMode(
QCompleter.CompletionMode.PopupCompletion)
......@@ -322,7 +322,7 @@ class MPCodeView(QPlainTextEdit):
suggested_word_list = self._suggested_word_list()
# update the word list model
self.mp_code_string_list_model.setStringList(suggested_word_list)
self.mp_code_word_list_model.setStringList(suggested_word_list)
# set pop-up view to top
self.completer.popup().setCurrentIndex(
......@@ -334,12 +334,6 @@ class MPCodeView(QPlainTextEdit):
+ self.completer.popup().verticalScrollBar().sizeHint().width())
self.completer.complete(cursor_rectangle)
# intercept mouse press for cursor highlighting
@Slot(str)
def mousePressEvent(self, event):
super(MPCodeView, self).mousePressEvent(event)
self.mp_code_cursor_highlighter.highlight_selection()
def line_number_area_width(self):
digits = 1
count = max(1, self.blockCount())
......
......@@ -7,7 +7,7 @@ from PySide6.QtCore import QSize
from PySide6.QtGui import QTextOption, QFont, QPainter
from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QDialog, QAbstractItemView, QPlainTextEdit
from mp_code_highlighter import MPCodeHighlighter
from mp_code_syntax_highlighter import MPCodeSyntaxHighlighter
from search_mp_code_dialog import Ui_SearchMPCodeDialog
from settings_manager import settings
......@@ -159,7 +159,7 @@ class SearchMPCodeDialogWrapper(QDialog):
self.document.setPlainText(mp_code_text)
# highlighter presence is enough to activate highlighting
self.highlighter = MPCodeHighlighter(self.document,
self.highlighter = MPCodeSyntaxHighlighter(self.document,
settings_manager.signal_settings_changed)
# connect buttons
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment