From ed3524d4ac896c8610b641e313101127752fbd26 Mon Sep 17 00:00:00 2001 From: Niels Vaes Date: Fri, 15 Mar 2024 18:04:50 +0100 Subject: [PATCH] * Various bug fixes * Hook file will be automatically copied every time the application starts * Stability improvements when executing code in the game. Whenever DCS needs to run code, there's a chance it will pause for a fraction of a second. Ideally the server runs in a separate thread, but I don't have the energy to research how to make a compiled C++ DLL right now to do that. Have to look into the Olympus guys' code maybe. Anyway, never eat yellow snow. --- CHANGELOG.md | 5 + dcs-code-injector-hook.lua | 2 +- dcs_code_injector/__init__.py | 2 +- dcs_code_injector/code_editor.py | 165 +++++++++++++----- dcs_code_injector/dcs_code_injector_window.py | 20 +-- dcs_code_injector/hook_string.py | 2 +- 6 files changed, 131 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a68e1d5..b1ee690 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.4.4 +* Various bug fixes +* Hook file will be automatically copied every time the application starts +* Stability improvements when executing code in the game. Whenever DCS needs to run code, there's a chance it will pause for a fraction of a second. Ideally the server runs in a separate thread, but I don't have the energy to research how to make a compiled C++ DLL right now to do that. Have to look into the Olympus guys' code maybe. Anyway, never eat yellow snow. + ## 1.4.3 * Keeping indentation when pressing enter. * Backspace will remove any leading 4 x space to jump back an indentation level diff --git a/dcs-code-injector-hook.lua b/dcs-code-injector-hook.lua index 60bb8f0..17dc22b 100644 --- a/dcs-code-injector-hook.lua +++ b/dcs-code-injector-hook.lua @@ -27,7 +27,7 @@ local function init() local code_injector_client = DCSCI.server:accept() if code_injector_client then - code_injector_client:settimeout(0) + code_injector_client:settimeout(0.005) local chunk, err, partial local response = "" diff --git a/dcs_code_injector/__init__.py b/dcs_code_injector/__init__.py index aa7d5ed..3da7a22 100644 --- a/dcs_code_injector/__init__.py +++ b/dcs_code_injector/__init__.py @@ -1,4 +1,4 @@ import os -VERSION = "1.4.3" +VERSION = "1.4.4" ICON = os.path.join(os.path.dirname(__file__), "ui", "icons", "icon.png") \ No newline at end of file diff --git a/dcs_code_injector/code_editor.py b/dcs_code_injector/code_editor.py index 82ce66a..6d8e5d5 100644 --- a/dcs_code_injector/code_editor.py +++ b/dcs_code_injector/code_editor.py @@ -10,6 +10,7 @@ class CodeTextEdit(QPlainTextEdit): + execute_code = Signal(str) def __init__(self): """ Constructor for the CodeTextEdit class. @@ -157,10 +158,11 @@ def update_line_number_area_width(self): def update_line_number_area(self, rect, dy): """ + Updates the line number area when the document is scrolled or the editor is resized. - :param rect: - :param dy: - :return: + Parameters: + rect (QRect): The rectangle in the viewport that needs to be updated. + dy (int): The amount of vertical scroll. If non-zero, the line number area is scrolled accordingly. """ if dy: self.line_number_area.scroll(0, dy) @@ -260,14 +262,29 @@ def update_keywords(self): list(set(self.completer.model().stringList() + [function_signature]))) def set_font(self, font): + """ + Sets the font used in the text editor. + + Parameters: + font (str): The name of the font to be set. + """ self.font = font EZSettings().set(sk.code_font, font) def set_font_size(self, font_size): + """ + Sets the font size used in the text editor. + + Parameters: + font_size (int): The size of the font to be set. + """ self.font_size = font_size EZSettings().set(sk.code_font_size, font_size) def update_font(self): + """ + Applies the current font and font size settings to the text editor. + """ self.setStyleSheet(f"font: {self.font_size}pt '{self.font}';") def __insert_code(self, text, move_back_pos): @@ -287,6 +304,9 @@ def __insert_code(self, text, move_back_pos): self.insertPlainText(selected_text) def check_cursor_position(self): + """ + Checks if the cursor has moved to a new block (line) and updates the autocompletion keywords if necessary. + """ cursor = self.textCursor() block_number = cursor.blockNumber() if block_number != self.previous_block_number: @@ -294,57 +314,78 @@ def check_cursor_position(self): self.update_keywords() def mousePressEvent(self, event): + """ + Overrides the mouse press event to check the cursor position when the mouse is clicked. + + Parameters: + event (QMouseEvent): The mouse event. + """ super().mousePressEvent(event) self.check_cursor_position() def keyPressEvent(self, event: QKeyEvent) -> None: - if self.completer.popup().isVisible() and event.key() in [ - Qt.Key.Key_Enter, - Qt.Key.Key_Return, - Qt.Key.Key_Up, - Qt.Key.Key_Down, - Qt.Key.Key_Tab, - Qt.Key.Key_Backtab, - ]: - self.completer.popup().close() - event.ignore() - return + """ + Overrides the key press event to add custom behavior for certain key combinations. - if event.key() == Qt.Key_Slash and event.modifiers() == Qt.ControlModifier: - self.handle_control_slash() - elif event.key() == Qt.Key_Up and event.modifiers() == Qt.ControlModifier: - self.handle_control_up() - elif event.key() == Qt.Key_Down and event.modifiers() == Qt.ControlModifier: - self.handle_control_down() - elif event.key() == Qt.Key_P and event.modifiers() == Qt.ControlModifier: - self.handle_control_p() - elif event.key() == Qt.Key_Tab: - self.handle_tab() - elif event.key() == Qt.Key_Backtab: - self.handle_backtab() - elif event.key() in (Qt.Key_Up, Qt.Key_Down): - super().keyPressEvent(event) - self.check_cursor_position() - elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: - cursor = self.textCursor() - current_line = cursor.block().text() - indentation = re.match(r"\s*", current_line).group() # Capture leading whitespace - super().keyPressEvent(event) # Call the parent method to insert the newline - self.insertPlainText(indentation) # Insert the captured indentation - self.check_cursor_position() - elif event.key() == Qt.Key_Backspace: - cursor = self.textCursor() - text_up_to_cursor = self.toPlainText()[:cursor.position()] - if text_up_to_cursor.endswith(' '): # Check if the cursor is preceded by 4 spaces - cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 4) - cursor.removeSelectedText() - return # Prevent calling super().keyPressEvent(event) to avoid deleting an extra character + Parameters: + event (QKeyEvent): The key event. + """ + if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return and event.modifiers() == Qt.ControlModifier: + text = self.textCursor().selection().toPlainText() + if text == "": + text = self.toPlainText() + self.execute_code.emit(text) + else: + if self.completer.popup().isVisible() and event.key() in [ + Qt.Key.Key_Enter, + Qt.Key.Key_Return, + Qt.Key.Key_Up, + Qt.Key.Key_Down, + Qt.Key.Key_Tab, + Qt.Key.Key_Backtab, + ]: + self.completer.popup().close() + event.ignore() + return + + if event.key() == Qt.Key_Slash and event.modifiers() == Qt.ControlModifier: + self.handle_control_slash() + elif event.key() == Qt.Key_Up and event.modifiers() == Qt.ControlModifier: + self.handle_control_up() + elif event.key() == Qt.Key_Down and event.modifiers() == Qt.ControlModifier: + self.handle_control_down() + elif event.key() == Qt.Key_P and event.modifiers() == Qt.ControlModifier: + self.handle_control_p() + elif event.key() == Qt.Key_Tab: + self.handle_tab() + elif event.key() == Qt.Key_Backtab: + self.handle_backtab() + elif event.key() in (Qt.Key_Up, Qt.Key_Down): + super().keyPressEvent(event) + self.check_cursor_position() + elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter and not event.modifiers() == Qt.ControlModifier: + cursor = self.textCursor() + current_line = cursor.block().text() + indentation = re.match(r"\s*", current_line).group() # Capture leading whitespace + super().keyPressEvent(event) # Call the parent method to insert the newline + self.insertPlainText(indentation) # Insert the captured indentation + self.check_cursor_position() + elif event.key() == Qt.Key_Backspace: + cursor = self.textCursor() + text_up_to_cursor = self.toPlainText()[:cursor.position()] + if text_up_to_cursor.endswith(' '): # Check if the cursor is preceded by 4 spaces + cursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 4) + cursor.removeSelectedText() + return # Prevent calling super().keyPressEvent(event) to avoid deleting an extra character + else: + super().keyPressEvent(event) else: super().keyPressEvent(event) - else: - super().keyPressEvent(event) def handle_control_slash(self): + """ + Toggles commenting on the selected lines. Adds or removes '-- ' at the start of each selected line. + """ cursor = self.textCursor() selected_text = cursor.selection().toPlainText() lines = selected_text.split("\n") @@ -359,18 +400,40 @@ def handle_control_slash(self): # replace the selected text with the commented lines cursor.insertText("\n".join(commented_lines)) + def update_document_size(self): + """ + Updates the document size based on the current font size, affecting the overall layout and appearance. + """ + + self.setStyleSheet(f"font: {self.font_size}pt 'Courier New';") + def handle_control_up(self): + """ + Increases the font size of the text editor by one point. + """ self.font_size += 1 self.update_document_size() def handle_control_down(self): + """ + Decreases the font size of the text editor by one point. + """ self.font_size -= 1 self.update_document_size() def handle_control_p(self): + """ + Inserts a predefined piece of code at the current cursor position. This is a placeholder for custom functionality. + """ self.__insert_code("BASE:I()", -1) def handle_special_characters(self, event): + """ + Handles the automatic insertion of paired special characters (e.g., quotes, parentheses). + + Parameters: + event (QKeyEvent): The key event containing the special character to be handled. + """ cursor = self.textCursor() cursor.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.KeepAnchor) if cursor.selectedText() == event.text(): @@ -390,6 +453,9 @@ def handle_special_characters(self, event): self.__insert_code(")" * 2, -1) def handle_tab(self): + """ + Handles the Tab key press for indentation. If text is selected, indents the selection; otherwise, inserts spaces. + """ cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() @@ -415,6 +481,9 @@ def handle_tab(self): self.setTextCursor(cursor) def handle_backtab(self): + """ + Handles the Shift+Tab key press for unindentation. If text is selected, unindents the selection. + """ cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() @@ -444,6 +513,12 @@ def handle_backtab(self): self.setTextCursor(cursor) def resizeEvent(self, event): + """ + Handles the resize event of the text editor to adjust the line number area accordingly. + + Parameters: + event (QResizeEvent): The resize event. + """ super().resizeEvent(event) content_rect = self.contentsRect() diff --git a/dcs_code_injector/dcs_code_injector_window.py b/dcs_code_injector/dcs_code_injector_window.py index fc9bd77..038ec70 100644 --- a/dcs_code_injector/dcs_code_injector_window.py +++ b/dcs_code_injector/dcs_code_injector_window.py @@ -1,7 +1,6 @@ from PySide6.QtWidgets import * from PySide6.QtGui import * from PySide6.QtCore import * -from PySide6.QtCore import Qt import os import json @@ -73,13 +72,11 @@ def __init__(self): self.timer.start() self.back_up_settings_file() + self.copy_hook_file() self.show() self.init_done = True - # def update_code_views_font(self): - # for i in range(self.tab_widget.count()): - # self.tab_widget.widget(i).update_font() def read_log(self): """ @@ -213,6 +210,7 @@ def add_new_tab(self, name=None, code=None): code_text_edit = CodeTextEdit() code_text_edit.textChanged.connect(self.save_code) + code_text_edit.execute_code.connect(self.send_code) self.tab_widget.insertTab(self.tab_widget.count() - 1, code_text_edit, "UNNAMED") self.tab_widget.setCurrentIndex(self.tab_widget.count() - 2) @@ -291,6 +289,7 @@ def add_text_to_log(self, complete_text): self.txt_log.verticalScrollBar().setValue(self.txt_log.verticalScrollBar().maximum()) def send_code(self, code): + self.add_code_to_log(code) self.statusbar.showMessage("Trying to send data...") self.stop_button = QPushButton("Stop") self.statusbar.addPermanentWidget(self.stop_button) @@ -397,19 +396,6 @@ def show_settings(): dlg = SettingsDialog() dlg.exec_() - def keyPressEvent(self, event: QKeyEvent) -> None: - """ - Handles key press events. - - :param event: the key press event - """ - - if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return and event.modifiers() == Qt.ControlModifier: - text = self.tab_widget.currentWidget().textCursor().selection().toPlainText() - if text == "": - text = self.tab_widget.currentWidget().toPlainText() - self.send_code(text) - def closeEvent(self, event): """ Handles the close event of the window, saves the window's position and size before closing diff --git a/dcs_code_injector/hook_string.py b/dcs_code_injector/hook_string.py index bc78793..029c59c 100644 --- a/dcs_code_injector/hook_string.py +++ b/dcs_code_injector/hook_string.py @@ -28,7 +28,7 @@ local code_injector_client = DCSCI.server:accept() if code_injector_client then - code_injector_client:settimeout(0) + code_injector_client:settimeout(0.005) local chunk, err, partial local response = ""