diff --git a/CHANGELOG.md b/CHANGELOG.md index b62cb6e..dab732b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,6 @@ * Fix a few small matters identified with tests * Make locales dynamically detected from the locales dir rather than hardcoded * Add version information in the navigation - * Increase line spacing between lines (except for code blocks) - * Add Italian translations (thanks @mdaleo404) # 0.2.1.8 diff --git a/bouquin/locales/it.json b/bouquin/locales/it.json deleted file mode 100644 index 5c9f8c0..0000000 --- a/bouquin/locales/it.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "db_sqlcipher_integrity_check_failed": "Controllo di integrità SQLCipher fallito", - "db_issues_reported": "problema/i segnalato/i", - "db_reopen_failed_after_rekey": "Riapertura fallita dopo il cambio chiave", - "db_version_id_does_not_belong_to_the_given_date": "version_id non appartiene alla data indicata", - "db_key_incorrect": "La chiave è probabilmente errata", - "db_database_error": "Errore del database", - "database_path": "Percorso del database", - "database_maintenance": "Manutenzione del database", - "database_compact": "Compatta il database", - "database_compact_explanation": "La compattazione esegue VACUUM sul database. Può aiutare a ridurne le dimensioni.", - "database_compacted_successfully": "Database compattato con successo!", - "encryption": "Crittografia", - "remember_key": "Ricorda la chiave", - "change_encryption_key": "Cambia chiave di crittografia", - "enter_a_new_encryption_key": "Inserisci una nuova chiave di crittografia", - "reenter_the_new_key": "Reinserisci la nuova chiave", - "key_mismatch": "Le chiavi non corrispondono", - "key_mismatch_explanation": "Le due chiavi inserite non corrispondono.", - "empty_key": "Chiave vuota", - "empty_key_explanation": "La chiave non può essere vuota.", - "key_changed": "Chiave cambiata", - "key_changed_explanation": "Il blocco note è stato criptato nuovamente con la nuova chiave!", - "error": "Errore", - "success": "Successo", - "close": "Chiudi", - "find": "Trova", - "file": "File", - "locale": "Lingua", - "locale_restart": "Per favore riavvia l'applicazione per caricare la nuova lingua.", - "settings": "Impostazioni", - "theme": "Tema", - "system": "Sistema", - "light": "Chiaro", - "dark": "Scuro", - "behaviour": "Comportamento", - "never": "Mai", - "browse": "Sfoglia", - "previous": "Precedente", - "previous_day": "Giorno precedente", - "next": "Successivo", - "next_day": "Giorno successivo", - "today": "Oggi", - "show": "Mostra", - "history": "Cronologia", - "view_history": "Visualizza cronologia", - "export": "Esporta", - "export_accessible_flag": "&Esporta", - "export_entries": "Esporta voci", - "export_complete": "Esportazione completata", - "export_failed": "Esportazione fallita", - "backup": "Backup", - "backup_complete": "Backup completato", - "backup_failed": "Backup fallito", - "quit": "Esci", - "help": "Aiuto", - "saved": "Salvato", - "saved_to": "Salvato in", - "documentation": "Documentazione", - "couldnt_open": "Impossibile aprire", - "report_a_bug": "Segnala un bug", - "version": "Versione", - "navigate": "Naviga", - "current": "corrente", - "selected": "selezionato", - "find_on_page": "Trova nella pagina", - "find_next": "Trova successivo", - "find_previous": "Trova precedente", - "find_bar_type_to_search": "Digita per cercare", - "find_bar_match_case": "Distingui maiuscole/minuscole", - "history_dialog_preview": "Anteprima", - "history_dialog_diff": "Differenze", - "history_dialog_revert_to_selected": "Ripristina alla versione selezionata", - "history_dialog_revert_failed": "Ripristino fallito", - "key_prompt_enter_key": "Inserisci la chiave", - "lock_overlay_locked_due_to_inactivity": "Bloccato per inattività", - "lock_overlay_unlock": "Sblocca", - "main_window_ready": "Pronto", - "main_window_save_a_version": "Salva una versione", - "main_window_settings_accessible_flag": "Impo&stazioni", - "set_an_encryption_key": "Imposta una chiave di crittografia", - "set_an_encryption_key_explanation": "Bouquin cripta i tuoi dati.\n\nCrea una passphrase sicura per criptare il blocco note.\n\nPuoi sempre cambiarla in seguito!", - "unlock_encrypted_notebook": "Sblocca il blocco note criptato", - "unlock_encrypted_notebook_explanation": "Inserisci la chiave per sbloccare il blocco note", - "open_in_new_tab": "Apri in una nuova scheda", - "autosave": "salvataggio automatico", - "unchecked_checkbox_items_moved_to_next_day": "Le caselle non spuntate sono state spostate al giorno successivo", - "move_yesterdays_unchecked_todos_to_today_on_startup": "Sposta i TODO non completati di ieri a oggi all'avvio", - "insert_images": "Inserisci immagini", - "images": "Immagini", - "reopen_failed": "Riapertura fallita", - "unlock_failed": "Sblocco fallito", - "could_not_unlock_database_at_new_path": "Impossibile sbloccare il database nel nuovo percorso.", - "unencrypted_export": "Esportazione non criptata", - "unencrypted_export_warning": "L'esportazione del database sarà non criptata!\nVuoi davvero continuare?\nSe desideri un backup criptato, scegli Backup invece di Esporta.", - "unrecognised_extension": "Estensione non riconosciuta!", - "backup_encrypted_notebook": "Backup del blocco note criptato", - "enter_a_name_for_this_version": "Inserisci un nome per questa versione", - "new_version_i_saved_at": "Nuova versione salvata il", - "save_key_warning": "Se non vuoi che ti venga richiesta la chiave di crittografia, seleziona questa opzione per ricordarla.\nATTENZIONE: la chiave viene salvata sul disco e potrebbe essere recuperabile se il disco fosse compromesso.", - "lock_screen_when_idle": "Blocca lo schermo quando inattivo", - "autolock_explanation": "Bouquin bloccherà automaticamente il blocco note dopo questo intervallo di tempo, dopodiché sarà necessario reinserire la chiave per sbloccarlo.\nImposta a 0 (mai) per non bloccarlo mai.", - "search_for_notes_here": "Cerca note qui", - "toolbar_format": "Formato", - "toolbar_bold": "Grassetto", - "toolbar_italic": "Corsivo", - "toolbar_strikethrough": "Barrato", - "toolbar_normal_paragraph_text": "Testo normale", - "toolbar_bulleted_list": "Elenco puntato", - "toolbar_numbered_list": "Elenco numerato", - "toolbar_code_block": "Blocco di codice", - "toolbar_heading": "Titolo", - "toolbar_toggle_checkboxes": "Attiva/disattiva caselle di controllo" -} diff --git a/bouquin/markdown_editor.py b/bouquin/markdown_editor.py index bd2bb98..a38ca1f 100644 --- a/bouquin/markdown_editor.py +++ b/bouquin/markdown_editor.py @@ -12,7 +12,6 @@ from PySide6.QtGui import ( QTextCursor, QTextDocument, QTextFormat, - QTextBlockFormat, QTextImageFormat, ) from PySide6.QtCore import Qt, QRect, QTimer @@ -44,8 +43,6 @@ class MarkdownEditor(QTextEdit): font.setPointSize(10) self.setFont(font) - self._apply_line_spacing() # 1.25× initial spacing - # Checkbox characters (Unicode for display, markdown for storage) self._CHECK_UNCHECKED_DISPLAY = "☐" self._CHECK_CHECKED_DISPLAY = "☑" @@ -76,8 +73,6 @@ class MarkdownEditor(QTextEdit): # reattach the highlighter to the new document if hasattr(self, "highlighter") and self.highlighter: self.highlighter.setDocument(self.document()) - self._apply_line_spacing() - self._apply_code_block_spacing() QTimer.singleShot(0, self._update_code_block_row_backgrounds) def showEvent(self, e): @@ -145,10 +140,9 @@ class MarkdownEditor(QTextEdit): def _update_code_block_row_backgrounds(self): """Paint a full-width background for each line that is in a fenced code block.""" doc = self.document() - if doc is None: - return - sels = [] + + # Use the same bg color as the highlighter's code block bg_brush = self.highlighter.code_block_format.background() inside = False @@ -164,12 +158,16 @@ class MarkdownEditor(QTextEdit): fmt = QTextCharFormat() fmt.setBackground(bg_brush) fmt.setProperty(QTextFormat.FullWidthSelection, True) + # mark so we can merge with other selections safely fmt.setProperty(QTextFormat.UserProperty, "codeblock_bg") sel.format = fmt cur = QTextCursor(doc) - cur.setPosition(block.position()) + cur.setPosition( + block.position() + ) # collapsed cursor = whole line when FullWidthSelection sel.cursor = cur + sels.append(sel) if is_fence: @@ -184,60 +182,6 @@ class MarkdownEditor(QTextEdit): ] self.setExtraSelections(others + sels) - def _apply_line_spacing(self, height: float = 125.0): - """Apply proportional line spacing to the whole document.""" - doc = self.document() - if doc is None: - return - - cursor = QTextCursor(doc) - cursor.beginEditBlock() - cursor.select(QTextCursor.Document) - - fmt = QTextBlockFormat() - fmt.setLineHeight( - height, # 125.0 = 1.25× - QTextBlockFormat.LineHeightTypes.ProportionalHeight.value, - ) - cursor.mergeBlockFormat(fmt) - cursor.endEditBlock() - - def _apply_code_block_spacing(self): - """ - Make all fenced code-block lines (including ``` fences) single-spaced. - Call this AFTER _apply_line_spacing(). - """ - doc = self.document() - if doc is None: - return - - cursor = QTextCursor(doc) - cursor.beginEditBlock() - - inside = False - block = doc.begin() - while block.isValid(): - text = block.text() - stripped = text.strip() - is_fence = stripped.startswith("```") - is_code_line = is_fence or inside - - if is_code_line: - fmt = block.blockFormat() - fmt.setLineHeight( - 0.0, - QTextBlockFormat.LineHeightTypes.SingleHeight.value, - ) - cursor.setPosition(block.position()) - cursor.setBlockFormat(fmt) - - if is_fence: - inside = not inside - - block = block.next() - - cursor.endEditBlock() - def to_markdown(self) -> str: """Export current content as markdown.""" # First, extract any embedded images and convert to markdown @@ -311,9 +255,6 @@ class MarkdownEditor(QTextEdit): finally: self._updating = False - self._apply_line_spacing() - self._apply_code_block_spacing() - # Render any embedded images self._render_images() @@ -511,33 +452,9 @@ class MarkdownEditor(QTextEdit): block_state = current_block.userState() - stripped = current_line.strip() - is_fence_line = stripped.startswith("```") - - if is_fence_line: - # Work out if this fence is closing (inside block before it) - inside_before = self._is_inside_code_block(current_block.previous()) - - # Insert the newline as usual - super().keyPressEvent(event) - - if inside_before: - # We were on the *closing* fence; the new line is outside the block. - # Give that new block normal 1.25× spacing. - new_block = self.textCursor().block() - fmt = new_block.blockFormat() - fmt.setLineHeight( - 125.0, - QTextBlockFormat.LineHeightTypes.ProportionalHeight.value, - ) - cur2 = self.textCursor() - cur2.setBlockFormat(fmt) - self.setTextCursor(cur2) - - return - - # Inside a code block (but not on a fence): newline stays code-style - if block_state == 1: + # If current line is opening code fence, or we're inside a code block + if current_line.strip().startswith("```") or block_state == 1: + # Just insert a regular newline - the highlighter will format it as code super().keyPressEvent(event) return @@ -729,9 +646,6 @@ class MarkdownEditor(QTextEdit): c.insertText(f"```\n{selected.rstrip()}\n```\n") if hasattr(self, "_update_code_block_row_backgrounds"): self._update_code_block_row_backgrounds() - # tighten spacing for the new code block - self._apply_code_block_spacing() - self.setFocus() return @@ -796,10 +710,6 @@ class MarkdownEditor(QTextEdit): if hasattr(self, "_update_code_block_row_backgrounds"): self._update_code_block_row_backgrounds() - - # tighten spacing for the new code block - self._apply_code_block_spacing() - self.setFocus() def apply_heading(self, size: int): diff --git a/tests/test_strings.py b/tests/test_strings.py index ec2c445..5aa79c9 100644 --- a/tests/test_strings.py +++ b/tests/test_strings.py @@ -5,13 +5,3 @@ def test_load_strings_uses_system_locale_and_fallback(): # pass a bogus locale to trigger fallback-to-default strings.load_strings("zz") assert strings._("next") # key exists in base translations - - -def test_load_strings_french(): - strings.load_strings("fr") - assert strings._("today") == "Aujourd'hui" # translation exists in French - - -def test_load_strings_italian(): - strings.load_strings("it") - assert strings._("today") == "Oggi" # translation exists in Italian