From a3b8769d867cd17db7c0a633d7f977885961fba3 Mon Sep 17 00:00:00 2001 From: Guilherme Costa Date: Fri, 2 Oct 2026 17:28:58 +0100 Subject: [PATCH] fix(notifications): keep line breaks in notification rows and split FlowGuard clog/tangle --- BlocksScreen/lib/klipper_message_filter.py | 9 +++++- BlocksScreen/lib/utils/list_model.py | 32 +++++++++++----------- tests/lib/test_klipper_message_filter.py | 13 +++++++++ tests/util/test_entry_delegate_unit.py | 15 ++++++++++ 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/BlocksScreen/lib/klipper_message_filter.py b/BlocksScreen/lib/klipper_message_filter.py index ea16b1bf..db7c22aa 100644 --- a/BlocksScreen/lib/klipper_message_filter.py +++ b/BlocksScreen/lib/klipper_message_filter.py @@ -287,7 +287,14 @@ def _re(pattern: str) -> Callable[[str], bool]: ), MessageRule( source=MessageSource.GCODE_ERROR, - matcher=_sub("flowguard"), + matcher=_sub("flowguard detected a clog"), + display="Filament Clog Detected", + hint="Check the extruder for a clog", + severity=Severity.ERROR, + ), + MessageRule( + source=MessageSource.GCODE_ERROR, + matcher=_sub("flowguard detected a tangle"), display="Filament Tangle Detected", hint="Clear the tangle", severity=Severity.ERROR, diff --git a/BlocksScreen/lib/utils/list_model.py b/BlocksScreen/lib/utils/list_model.py index 87d55b9d..dae04431 100644 --- a/BlocksScreen/lib/utils/list_model.py +++ b/BlocksScreen/lib/utils/list_model.py @@ -382,12 +382,17 @@ def sizeHint( text_avail_width = max(target_width - left_reserved - right_reserved, 50) - single_line_width = fm.horizontalAdvance(item.text) + collapsed_h = int(item.height * 1.1) + lines = item.text.split("\n") + # paint() insets the row by 2px per side before fitting lines + max_lines = max(1, (collapsed_h - 4) // fm.lineSpacing()) - item.needs_expansion = single_line_width > text_avail_width + item.needs_expansion = len(lines) > max_lines or any( + fm.horizontalAdvance(line) > text_avail_width for line in lines + ) if not item.is_expanded: - return QtCore.QSize(target_width, int(item.height * 1.1)) + return QtCore.QSize(target_width, collapsed_h) text_rect = fm.boundingRect( QtCore.QRect(0, 0, int(text_avail_width), 0), @@ -421,7 +426,6 @@ def paint( if item.allow_expand and item.needs_expansion: item.right_icon = self._expand_arrow(item.is_expanded) - # Background Color pressed_color = QtGui.QColor("#1A8FBF") pressed_color.setAlpha(90 if item.selected else 20) @@ -429,9 +433,6 @@ def paint( painter.setBrush(pressed_color) painter.fillPath(path, pressed_color) - # Geometry Calc - - # ICON SPACEEE ellipse_size = item.height * 0.8 ellipse_margin = (item.height - ellipse_size) / 2 ellipse_rect = QtCore.QRectF( @@ -503,14 +504,14 @@ def paint( - left_margin ) - text = item.text.replace("\n", "") - # Logic: If not expanded, OR if expansion is not needed, draw single line if not item.is_expanded: - max_main_text_width = right_text_x - left_margin - text = metrics.elidedText( - text, - QtCore.Qt.TextElideMode.ElideRight, - int(max_main_text_width), + max_main_text_width = int(right_text_x - left_margin) + max_lines = max(1, int(text_rect.height()) // metrics.lineSpacing()) + text = "\n".join( + metrics.elidedText( + line, QtCore.Qt.TextElideMode.ElideRight, max_main_text_width + ) + for line in item.text.split("\n")[:max_lines] ) painter.drawText( text_rect, @@ -518,13 +519,12 @@ def paint( text, ) else: - # Expanded mode painter.drawText( text_rect, QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter | QtCore.Qt.TextFlag.TextWordWrap, - text, + item.text, ) if item.right_text: diff --git a/tests/lib/test_klipper_message_filter.py b/tests/lib/test_klipper_message_filter.py index fcea2472..3597e99d 100644 --- a/tests/lib/test_klipper_message_filter.py +++ b/tests/lib/test_klipper_message_filter.py @@ -256,6 +256,19 @@ def test_re_no_match() -> None: Severity.ERROR, ), (MessageSource.GCODE_ERROR, "gate empty", "Spool Empty", Severity.WARNING), + ( + MessageSource.GCODE_ERROR, + "FlowGuard detected a clog.\nReason for trip: Compression stuck after " + "409.04 mm motion (triggering parameter: flowguard_max_relief)", + "Filament Clog Detected", + Severity.ERROR, + ), + ( + MessageSource.GCODE_ERROR, + "FlowGuard detected a tangle.\nReason for trip: Tension stuck", + "Filament Tangle Detected", + Severity.ERROR, + ), # Gcode Echo ( MessageSource.GCODE_ECHO, diff --git a/tests/util/test_entry_delegate_unit.py b/tests/util/test_entry_delegate_unit.py index fadee006..0d433374 100644 --- a/tests/util/test_entry_delegate_unit.py +++ b/tests/util/test_entry_delegate_unit.py @@ -198,3 +198,18 @@ def test_arrow_ignored_when_expansion_not_needed(self, delegate, model): x, y = _arrow_center(option) assert _tap(delegate, model, 0, x, y, option) is True assert model.data(model.index(0), EntryListModel.ExpandRole) is False + + +class TestNeedsExpansion: + def _size_hint(self, delegate, text): + item = ListItem(text=text, height=ROW_H) + model = EntryListModel([item]) + delegate.sizeHint(_option(0), model.index(0)) + return item + + def test_short_single_line_fits(self, delegate): + assert self._size_hint(delegate, "A").needs_expansion is False + + def test_more_lines_than_row_holds_needs_expansion(self, delegate): + text = "\n".join("line" for _ in range(20)) + assert self._size_hint(delegate, text).needs_expansion is True