Skip to content

Commit fe35918

Browse files
committed
Better tab widgets
1 parent 2781de1 commit fe35918

5 files changed

Lines changed: 135 additions & 176 deletions

File tree

activity_browser/ui/widgets/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@
2222
from .menu import ABMenu
2323
from .drop_overlay import ABDropOverlay
2424
from .tree_view import ABNewTreeView
25+
from .buttons import ABCloseButton, ABMinimizeButton
26+
from .tab_widget import ABTabWidget
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from qtpy import QtWidgets, QtCore, QtGui
2+
from qtpy.QtCore import Qt
3+
4+
5+
class ABCloseButton(QtWidgets.QWidget):
6+
"""Custom close button with hover effect."""
7+
clicked: QtCore.SignalInstance = QtCore.Signal()
8+
9+
def __init__(self, parent=None):
10+
super().__init__(parent)
11+
12+
13+
self.label = QtWidgets.QLabel("×", self)
14+
15+
self.label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
16+
self.label.setAlignment(Qt.AlignCenter)
17+
self.label.setFixedSize(16, 16)
18+
self.label.mousePressEvent = lambda event: self.clicked.emit()
19+
20+
self.label.setStyleSheet("""
21+
QLabel {
22+
border-radius: 8px;
23+
background-color: transparent;
24+
}
25+
QLabel:hover {
26+
background-color: rgba(255, 0, 0, 0.5);
27+
}
28+
""")
29+
30+
layout = QtWidgets.QHBoxLayout()
31+
layout.setContentsMargins(5, 0, 0, 0)
32+
layout.addWidget(self.label)
33+
self.setLayout(layout)
34+
35+
36+
class ABMinimizeButton(QtWidgets.QWidget):
37+
"""Custom close button with hover effect."""
38+
clicked: QtCore.SignalInstance = QtCore.Signal()
39+
40+
def __init__(self, parent=None):
41+
super().__init__(parent)
42+
43+
self.label = QtWidgets.QLabel("-", self)
44+
45+
self.label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
46+
self.label.setAlignment(Qt.AlignCenter)
47+
self.label.setFixedSize(16, 16)
48+
self.label.mousePressEvent = lambda event: self.clicked.emit()
49+
50+
self.setStyleSheet("""
51+
QLabel {
52+
border-radius: 8px;
53+
background-color: transparent;
54+
}
55+
QLabel:hover {
56+
background-color: rgba(42, 157, 244, 0.5);
57+
}
58+
""")
59+
60+
layout = QtWidgets.QHBoxLayout()
61+
layout.setContentsMargins(5, 0, 0, 0)
62+
layout.addWidget(self.label)
63+
self.setLayout(layout)

activity_browser/ui/widgets/central.py

Lines changed: 4 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,18 @@
11
from loguru import logger
22

3-
from qtpy import QtWidgets, QtCore
4-
from .dock_widget import CloseButton, MinimizeButton
3+
from qtpy import QtWidgets
54

5+
from .tab_widget import ABTabWidget
66

7-
class CentralTabBar(QtWidgets.QTabBar):
8-
"""Custom tab bar for the CentralTabWidget."""
9-
10-
def __init__(self, parent=None):
11-
super().__init__(parent)
12-
self.setMovable(True)
13-
self.setDocumentMode(True)
147

15-
16-
class CentralTabWidget(QtWidgets.QTabWidget):
8+
class CentralTabWidget(ABTabWidget):
179
"""
1810
A custom QTabWidget that manages groups of tabs and their associated pages.
1911
2012
This widget allows for organizing tabs into groups, dynamically adding pages to groups,
2113
and ensuring that each page has a unique object name.
2214
"""
2315

24-
def __init__(self, *args):
25-
"""
26-
Initialize the CentralTabWidget.
27-
28-
Args:
29-
*args: Additional positional arguments passed to the parent QTabWidget.
30-
"""
31-
super().__init__(*args)
32-
33-
# Use custom tab bar with close/minimize buttons
34-
self.setTabBar(CentralTabBar(self))
35-
36-
def addTab(self, widget, label, show_minimize=False):
37-
"""Override addTab to add custom buttons to each tab.
38-
39-
Args:
40-
widget: The widget to add as a tab
41-
label: The label for the tab
42-
show_minimize: If True, show minimize button; if False, show close button
43-
"""
44-
index = super().addTab(widget, label)
45-
self._add_tab_buttons(index, show_minimize)
46-
return index
47-
48-
def insertTab(self, index, widget, label, show_minimize=False):
49-
"""Override insertTab to add custom buttons to each tab.
50-
51-
Args:
52-
index: The index at which to insert the tab
53-
widget: The widget to add as a tab
54-
label: The label for the tab
55-
show_minimize: If True, show minimize button; if False, show close button
56-
"""
57-
index = super().insertTab(index, widget, label)
58-
self._add_tab_buttons(index, show_minimize)
59-
return index
60-
61-
def _add_tab_buttons(self, index, show_minimize=False):
62-
"""Add close OR minimize button to a tab (mutually exclusive).
63-
64-
Args:
65-
index: The index of the tab
66-
show_minimize: If True, show minimize button; otherwise show close button
67-
"""
68-
widget = self.widget(index)
69-
if not widget:
70-
return
71-
72-
# Create a widget to hold the button
73-
button_widget = QtWidgets.QWidget()
74-
button_layout = QtWidgets.QHBoxLayout(button_widget)
75-
button_layout.setContentsMargins(0, 0, 0, 0)
76-
button_layout.setSpacing(2)
77-
78-
# Add either minimize or close button (mutually exclusive)
79-
if show_minimize:
80-
minimize_btn = MinimizeButton(button_widget)
81-
minimize_btn.clicked.connect(lambda w=widget: self._minimize_tab_by_widget(w))
82-
button_layout.addWidget(minimize_btn)
83-
else:
84-
close_btn = CloseButton(button_widget)
85-
close_btn.clicked.connect(lambda w=widget: self._close_tab_by_widget(w))
86-
button_layout.addWidget(close_btn)
87-
88-
# Set the button widget on the tab
89-
self.tabBar().setTabButton(index, QtWidgets.QTabBar.ButtonPosition.RightSide, button_widget)
90-
91-
def _close_tab_by_widget(self, widget):
92-
"""Handle close button click using the widget reference."""
93-
index = self.indexOf(widget)
94-
if index >= 0:
95-
self.tabCloseRequested.emit(index)
96-
97-
def _minimize_tab_by_widget(self, widget):
98-
"""Handle minimize button click using the widget reference."""
99-
index = self.indexOf(widget)
100-
if index >= 0:
101-
self.tabCloseRequested.emit(index)
102-
10316
@property
10417
def groups(self):
10518
"""
@@ -152,7 +65,7 @@ def addToGroup(self, group: str, page: QtWidgets.QWidget):
15265
page.deleteLater() # Clean up the newly created page since it already exists
15366

15467

155-
class GroupTabWidget(QtWidgets.QTabWidget):
68+
class GroupTabWidget(ABTabWidget):
15669
"""
15770
A custom QTabWidget that represents a group of tabs.
15871
@@ -169,9 +82,6 @@ def __init__(self, name: str, *args):
16982
*args: Additional positional arguments passed to the parent QTabWidget.
17083
"""
17184
super().__init__(*args)
172-
self.setMovable(True) # Allow tabs to be rearranged.
173-
self.setTabsClosable(True) # Allow tabs to be closed.
174-
self.setDocumentMode(True) # Enable document mode for a more modern appearance.
17585

17686
self.setObjectName(name) # Set the object name for the widget.
17787

Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from qtpy import QtWidgets, QtCore, QtGui
1+
from qtpy import QtWidgets, QtGui
22
from qtpy.QtCore import Qt
33

4+
from .buttons import ABCloseButton, ABMinimizeButton
5+
46

57
class HideMode:
68
Close = 1
@@ -32,10 +34,10 @@ def setWidget(self, widget):
3234

3335
def button(self):
3436
if self._hide_mode == HideMode.Close:
35-
button = CloseButton(self)
37+
button = ABCloseButton(self)
3638
button.clicked.connect(self.close)
3739
else:
38-
button = MinimizeButton(self)
40+
button = ABMinimizeButton(self)
3941
button.clicked.connect(self.hide)
4042
return button
4143

@@ -64,82 +66,3 @@ def set_button(self, button):
6466
w.deleteLater()
6567

6668

67-
class CloseButton(QtWidgets.QWidget):
68-
"""Custom close button with hover effect."""
69-
clicked: QtCore.SignalInstance = QtCore.Signal()
70-
71-
def __init__(self, parent=None):
72-
super().__init__(parent)
73-
74-
75-
self.label = QtWidgets.QLabel("×", self)
76-
77-
self.label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
78-
self.label.setAlignment(Qt.AlignCenter)
79-
self.label.setFixedSize(16, 16)
80-
self.label.mousePressEvent = lambda event: self.clicked.emit()
81-
82-
self.label.setStyleSheet("""
83-
QLabel {
84-
border-radius: 8px;
85-
background-color: transparent;
86-
}
87-
QLabel:hover {
88-
background-color: rgba(255, 0, 0, 0.5);
89-
}
90-
""")
91-
92-
layout = QtWidgets.QHBoxLayout()
93-
layout.setContentsMargins(5, 0, 0, 0)
94-
layout.addWidget(self.label)
95-
self.setLayout(layout)
96-
97-
98-
class MinimizeButton(QtWidgets.QWidget):
99-
"""Custom close button with hover effect."""
100-
clicked: QtCore.SignalInstance = QtCore.Signal()
101-
102-
def __init__(self, parent=None):
103-
super().__init__(parent)
104-
105-
self.label = QtWidgets.QLabel("-", self)
106-
107-
self.label.setFont(QtGui.QFont("Arial", 12, QtGui.QFont.Bold))
108-
self.label.setAlignment(Qt.AlignCenter)
109-
self.label.setFixedSize(16, 16)
110-
self.label.mousePressEvent = lambda event: self.clicked.emit()
111-
112-
self.setStyleSheet("""
113-
QLabel {
114-
border-radius: 8px;
115-
background-color: transparent;
116-
}
117-
QLabel:hover {
118-
background-color: rgba(42, 157, 244, 0.5);
119-
}
120-
""")
121-
122-
layout = QtWidgets.QHBoxLayout()
123-
layout.setContentsMargins(5, 0, 0, 0)
124-
layout.addWidget(self.label)
125-
self.setLayout(layout)
126-
127-
128-
def mousePressEvent(self, event):
129-
if event.button() == Qt.LeftButton:
130-
self.drag_start_pos = event.pos()
131-
132-
133-
def mouseMoveEvent(self, event):
134-
if not self.drag_start_pos:
135-
return
136-
137-
# Check if mouse moved beyond threshold
138-
if (event.pos() - self.drag_start_pos).manhattanLength() > QtWidgets.QApplication.startDragDistance():
139-
index = self.tabAt(self.drag_start_pos)
140-
if index >= 0:
141-
startDrag(self, index)
142-
143-
def startDrag(self, index):
144-
"""Start dragging a tab."""
145-
print("Dragging success")
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from qtpy import QtWidgets
2+
3+
from .buttons import ABCloseButton, ABMinimizeButton
4+
5+
6+
class ABTabWidget(QtWidgets.QTabWidget):
7+
def __init__(self, name: str, *args):
8+
"""
9+
Initialize the GroupTabWidget.
10+
11+
Args:
12+
name (str): The name of the group, used as the object name for the widget.
13+
*args: Additional positional arguments passed to the parent QTabWidget.
14+
"""
15+
super().__init__(*args)
16+
self.setMovable(True) # Allow tabs to be rearranged.
17+
self.setTabsClosable(True) # Allow tabs to be closed.
18+
self.tabBar().setExpanding(False)
19+
20+
21+
def resizeEvent(self, event):
22+
super().resizeEvent(event)
23+
# Force the tab bar to always fill the full width
24+
self.tabBar().setMinimumWidth(self.width())
25+
26+
def addTab(self, widget, label, show_minimize=False):
27+
"""Override addTab to add custom buttons to each tab.
28+
29+
Args:
30+
widget: The widget to add as a tab
31+
label: The label for the tab
32+
show_minimize: If True, show minimize button; if False, show close button
33+
"""
34+
index = super().addTab(widget, label)
35+
self._set_buttons(index, widget, show_minimize)
36+
return index
37+
38+
def insertTab(self, index, widget, label, show_minimize=False):
39+
"""Override insertTab to add custom buttons to each tab.
40+
41+
Args:
42+
index: The index at which to insert the tab
43+
widget: The widget to add as a tab
44+
label: The label for the tab
45+
show_minimize: If True, show minimize button; if False, show close button
46+
"""
47+
index = super().insertTab(index, widget, label)
48+
self._set_buttons(index, widget, show_minimize)
49+
return index
50+
51+
def _set_buttons(self, index, widget, show_minimize=False):
52+
tab_bar = self.tabBar()
53+
button = ABMinimizeButton() if show_minimize else ABCloseButton()
54+
tab_bar.setTabButton(index, QtWidgets.QTabBar.ButtonPosition.RightSide, button)
55+
button.clicked.connect(lambda w=widget: self.closeTabByWidget(w))
56+
57+
def closeTabByWidget(self, widget):
58+
"""Handle close button click using the widget reference."""
59+
index = self.indexOf(widget)
60+
if index >= 0:
61+
self.tabCloseRequested.emit(index)

0 commit comments

Comments
 (0)