Coverage for vibefpmgui/widgets.py: 100%

70 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-11-01 20:51 +0000

1"""Small widgets shared by the window's parts.""" 

2 

3from qtpy.QtCore import QSize, Qt, Signal 

4from qtpy.QtWidgets import QDialog, QLabel, QLineEdit, QMenu, QPushButton, QSizePolicy 

5 

6 

7class StatusLabel(QLabel): 

8 """A label for a line of status whose text keeps changing (an editor's 

9 status line, a panel's progress): it asks for its text's width when 

10 there's room, but needs only a little. A plain label's minimum width is 

11 its whole text, so a longer status ("154 AI review notes", "… · context 

12 ~14k of 32k") made the window wider, past the screen's edge for a window 

13 filling it. When narrow, the text is cut off at the right.""" 

14 

15 MINIMUM_WIDTH = 40 

16 

17 def minimumSizeHint(self) -> QSize: 

18 hint = super().minimumSizeHint() 

19 return QSize(min(hint.width(), self.MINIMUM_WIDTH), hint.height()) 

20 

21 

22class HistoryLineEdit(QLineEdit): 

23 """A field for commands, as a shell's: Enter sends its text 

24 (``submitted``), remembers it, and clears the field; Up and Down go 

25 through what was sent (Down past the last one: what was being typed). 

26 The history is this field's own, for as long as it lives (not saved).""" 

27 

28 submitted = Signal(str) 

29 

30 def __init__(self, parent=None): 

31 super().__init__(parent) 

32 self.history: list[str] = [] 

33 self._index = 0 # (in history; len(history): the text being typed, not sent yet) 

34 self._draft = "" 

35 self.returnPressed.connect(self._submit) 

36 

37 def _submit(self): 

38 text = self.text().strip() 

39 if not text: 

40 return 

41 if not self.history or self.history[-1] != text: # (the same twice in a row: kept once) 

42 self.history.append(text) 

43 self._index, self._draft = len(self.history), "" 

44 self.clear() 

45 self.submitted.emit(text) 

46 

47 def keyPressEvent(self, event): 

48 key = event.key() 

49 if key == Qt.Key_Up and self._index > 0: 

50 if self._index == len(self.history): 

51 self._draft = self.text() 

52 self._index -= 1 

53 self.setText(self.history[self._index]) 

54 elif key == Qt.Key_Down and self._index < len(self.history): 

55 self._index += 1 

56 self.setText(self._draft if self._index == len(self.history) else self.history[self._index]) 

57 elif key in (Qt.Key_Up, Qt.Key_Down): 

58 pass # (no earlier, or later, entry) 

59 else: 

60 super().keyPressEvent(event) 

61 

62 

63def run_dialog(dialog: QDialog) -> int: 

64 """Show a dialog until it's closed, then let it go (a dialog that's a 

65 child of the window would otherwise stay, hidden, until the app quits).""" 

66 try: 

67 return dialog.exec() 

68 finally: 

69 dialog.deleteLater() 

70 

71 

72class StayOpenMenu(QMenu): 

73 """A menu that stays open when a checkable item is clicked (to tick several).""" 

74 

75 def mouseReleaseEvent(self, event): 

76 action = self.activeAction() 

77 if action is not None and action.isCheckable() and action.isEnabled(): 

78 action.trigger() 

79 return 

80 super().mouseReleaseEvent(event) 

81 

82 

83class MenuButton(QPushButton): 

84 """A button that opens a menu, its text cut short with "…" to fit (it 

85 doesn't make the panel it's in wider); the whole text is its tooltip.""" 

86 

87 def __init__(self, parent=None): 

88 super().__init__(parent) 

89 self._full_text = "" 

90 self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) 

91 

92 def set_full_text(self, text: str): 

93 self._full_text = text 

94 self._elide() 

95 

96 def full_text(self) -> str: 

97 return self._full_text 

98 

99 def minimumSizeHint(self): 

100 hint = super().minimumSizeHint() 

101 return QSize(60, hint.height()) 

102 

103 def sizeHint(self): 

104 hint = super().sizeHint() 

105 return QSize(60, hint.height()) 

106 

107 def resizeEvent(self, event): 

108 super().resizeEvent(event) 

109 self._elide() 

110 

111 def _elide(self): 

112 room = max(10, self.width() - 40) # (the menu arrow and the margins) 

113 self.setText(self.fontMetrics().elidedText(self._full_text, Qt.ElideRight, room))