Skip to content

Commit e2b6e7d

Browse files
committed
feat: show cursor position and char count in status bar
1 parent b7138e7 commit e2b6e7d

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

src/views/main_window.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ def _load_ui(self) -> None:
177177
-1,
178178
)
179179

180+
# Permanent status bar label — lives on the right, never overwritten by showMessage()
181+
self._cursor_label = QLabel("Ln 1, Col 1 | 0 chars")
182+
self.ui.statusBar().addPermanentWidget(self._cursor_label)
183+
180184
def _setup_file_tree(self) -> None:
181185
"""Configure QFileSystemModel rooted at the user's home directory."""
182186
self._fs_model = QFileSystemModel(self.ui)
@@ -239,6 +243,9 @@ def _connect_signals(self) -> None:
239243
lambda _: self._update_title()
240244
)
241245

246+
# Cursor position: update permanent label on every cursor move
247+
self._plain_text_edit.cursorPositionChanged.connect(self._update_cursor_label)
248+
242249
# Keyboard shortcuts not present in the .ui file.
243250
# (Ctrl+S/O/Q/Shift+S are already wired via QAction shortcuts in main_window.ui.)
244251
# ApplicationShortcut context: fires even when a child widget holds focus,
@@ -378,6 +385,14 @@ def _update_title(self) -> None:
378385
suffix = " *" if modified else ""
379386
self.ui.setWindowTitle(f"TextTools — {name}{suffix}" if name else "TextTools")
380387

388+
def _update_cursor_label(self) -> None:
389+
"""Update the permanent cursor position label in the status bar."""
390+
cursor = self._plain_text_edit.textCursor()
391+
line = cursor.blockNumber() + 1
392+
col = cursor.columnNumber() + 1
393+
chars = len(self._plain_text_edit.toPlainText())
394+
self._cursor_label.setText(f"Ln {line}, Col {col} | {chars:,} chars")
395+
381396
# ------------------------------------------ ViewModel signal handlers
382397

383398
def _on_document_loaded(self, content: str) -> None:

tests/integration/test_main_window.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,3 +415,28 @@ def test_convert_button_calls_viewmodel(self, window, qtbot):
415415
with patch.object(window._viewmodel, "convert_to_utf8") as mock_convert:
416416
window._convert_button.click()
417417
mock_convert.assert_called_once_with("hello")
418+
419+
420+
class TestStatusBarCursorPosition:
421+
def test_cursor_label_exists(self, window):
422+
"""A permanent cursor label must be visible in the status bar."""
423+
assert hasattr(window, "_cursor_label")
424+
assert window._cursor_label.text() != ""
425+
426+
def test_cursor_label_shows_line_and_col(self, window, qtbot):
427+
"""Moving the cursor must update the cursor label."""
428+
window._plain_text_edit.setPlainText("first line\nsecond line")
429+
# Move cursor to start of second line (position 11)
430+
cursor = window._plain_text_edit.textCursor()
431+
cursor.setPosition(11)
432+
window._plain_text_edit.setTextCursor(cursor)
433+
qtbot.wait(10)
434+
label_text = window._cursor_label.text()
435+
assert "Ln 2" in label_text
436+
assert "Col 1" in label_text
437+
438+
def test_cursor_label_shows_char_count(self, window, qtbot):
439+
"""The cursor label must include the document character count."""
440+
window._plain_text_edit.setPlainText("hello")
441+
qtbot.wait(10)
442+
assert "5" in window._cursor_label.text()

0 commit comments

Comments
 (0)