1 """Provides quick switcher"""
4 from qtpy
import QtCore
6 from qtpy
.QtCore
import Qt
7 from qtpy
.QtCore
import Signal
10 from ..widgets
import defs
11 from ..widgets
import standard
12 from ..widgets
import text
15 def switcher_inner_view(
16 context
, entries
, title
=None, place_holder
=None, enter_action
=None, parent
=None
18 dialog
= SwitcherInnerView(
19 context
, entries
, title
, place_holder
, enter_action
, parent
25 def switcher_outer_view(context
, entries
, place_holder
=None, parent
=None):
26 dialog
= SwitcherOuterView(context
, entries
, place_holder
, parent
)
31 def switcher_item(key
, icon
=None, name
=None):
32 return SwitcherListItem(key
, icon
, name
)
36 selection_move_keys
= [
46 return selection_move_keys
49 class Switcher(standard
.Dialog
):
51 Quick switcher base class. This contains input field, filter proxy model.
52 This will be inherited by outer-view class(SwitcherOuterView) or inner-view class
55 inner-view class is a quick-switcher widget including view. In this case, this
56 switcher will have switcher_list field, and show the items list in itself.
57 outer-view class is a quick-switcher widget without view(only input field), which
58 means sharing model with other view class.
60 switcher_selection_move signal is for the event that selection move key like UP,
61 DOWN has pressed while focusing on input field.
71 standard
.Dialog
.__init
__(self
, parent
=parent
)
73 self
.context
= context
74 self
.entries_model
= entries_model
76 self
.filter_input
= SwitcherLineEdit(place_holder
=place_holder
, parent
=self
)
78 self
.proxy_model
= SwitcherSortFilterProxyModel(entries_model
, parent
=self
)
79 self
.switcher_list
= None
81 self
.filter_input
.textChanged
.connect(self
.filter_input_changed
)
83 def filter_input_changed(self
):
84 input_text
= self
.filter_input
.text()
85 pattern
= '.*'.join(re
.escape(c
) for c
in input_text
)
86 self
.proxy_model
.setFilterRegExp(pattern
)
89 class SwitcherInnerView(Switcher
):
103 place_holder
=place_holder
,
106 self
.setWindowTitle(title
)
107 if parent
is not None:
108 self
.setWindowModality(Qt
.WindowModal
)
110 self
.enter_action
= enter_action
111 self
.switcher_list
= SwitcherTreeView(
112 self
.proxy_model
, self
.enter_selected_item
, parent
=self
115 self
.main_layout
= qtutils
.vbox(
116 defs
.no_margin
, defs
.spacing
, self
.filter_input
, self
.switcher_list
118 self
.setLayout(self
.main_layout
)
120 # moving key has pressed while focusing on input field
121 self
.filter_input
.switcher_selection_move
.connect(
122 self
.switcher_list
.keyPressEvent
124 # escape key pressed while focusing on input field
125 self
.filter_input
.switcher_escape
.connect(self
.close
)
126 self
.filter_input
.switcher_accept
.connect(self
.accept_selected_item
)
127 # some key except moving key has pressed while focusing on list view
128 self
.switcher_list
.switcher_inner_text
.connect(self
.filter_input
.keyPressEvent
)
130 # default selection for first index
131 first_proxy_idx
= self
.proxy_model
.index(0, 0)
132 self
.switcher_list
.setCurrentIndex(first_proxy_idx
)
134 self
.set_initial_geometry(parent
)
136 def accept_selected_item(self
):
137 item
= self
.switcher_list
.selected_item()
139 self
.enter_action(item
)
142 def set_initial_geometry(self
, parent
):
143 """Set the initial size and position"""
147 width
= parent
.width()
148 height
= parent
.height()
149 self
.resize(max(width
* 2 // 3, 320), max(height
* 2 // 3, 240))
151 def enter_selected_item(self
, index
):
152 item
= self
.switcher_list
.model().itemFromIndex(index
)
154 self
.enter_action(item
)
158 class SwitcherOuterView(Switcher
):
159 def __init__(self
, context
, entries_model
, place_holder
=None, parent
=None):
164 place_holder
=place_holder
,
167 self
.filter_input
.hide()
169 self
.main_layout
= qtutils
.vbox(defs
.no_margin
, defs
.spacing
, self
.filter_input
)
170 self
.setLayout(self
.main_layout
)
172 def filter_input_changed(self
):
173 super().filter_input_changed()
174 # Hide the input when it becomes empty.
175 input_text
= self
.filter_input
.text()
177 self
.filter_input
.hide()
180 class SwitcherLineEdit(text
.LineEdit
):
181 """Quick switcher input line class"""
183 # signal is for the event that selection move key like UP, DOWN has pressed
184 # while focusing on this line edit widget
185 switcher_selection_move
= Signal(QtGui
.QKeyEvent
)
186 switcher_visible
= Signal(bool)
187 switcher_accept
= Signal()
188 switcher_escape
= Signal()
190 def __init__(self
, place_holder
=None, parent
=None):
191 text
.LineEdit
.__init
__(self
, parent
=parent
)
193 self
.setPlaceholderText(place_holder
)
195 def keyPressEvent(self
, event
):
197 To be able to move the selection while focus on the input field, input text
198 field should be able to filter pressed key.
199 If pressed key is moving selection key like UP or DOWN, the
200 switcher_selection_move signal will be emitted and the view selection
201 will be moved regardless whether Switcher is inner-view or outer-view.
202 Or else, simply act like text input to the field.
204 selection_moving_keys
= moving_keys()
205 pressed_key
= event
.key()
207 if pressed_key
== Qt
.Key_Escape
:
208 self
.switcher_escape
.emit()
209 elif pressed_key
in (Qt
.Key_Enter
, Qt
.Key_Return
):
210 self
.switcher_accept
.emit()
211 elif pressed_key
in selection_moving_keys
:
212 self
.switcher_selection_move
.emit(event
)
214 super().keyPressEvent(event
)
217 class SwitcherSortFilterProxyModel(QtCore
.QSortFilterProxyModel
):
218 """Filtering class for candidate items."""
220 def __init__(self
, entries
, parent
=None):
221 QtCore
.QSortFilterProxyModel
.__init
__(self
, parent
)
223 self
.entries
= entries
225 self
.setDynamicSortFilter(True)
226 self
.setSourceModel(entries
)
227 self
.setFilterCaseSensitivity(Qt
.CaseInsensitive
)
229 def itemFromIndex(self
, index
):
230 return self
.entries
.itemFromIndex(self
.mapToSource(index
))
233 # pylint: disable=too-many-ancestors
234 class SwitcherTreeView(standard
.TreeView
):
235 """Tree view class for showing proxy items in SwitcherSortFilterProxyModel"""
237 # signal is for the event that some key except moving key has pressed
238 # while focusing this view
239 switcher_inner_text
= Signal(QtGui
.QKeyEvent
)
241 def __init__(self
, entries_proxy_model
, enter_action
, parent
=None):
242 standard
.TreeView
.__init
__(self
, parent
)
244 self
.setHeaderHidden(True)
245 self
.setModel(entries_proxy_model
)
247 self
.doubleClicked
.connect(enter_action
)
249 def keyPressEvent(self
, event
):
250 """hooks when a key has pressed while focusing on list view"""
251 selection_moving_keys
= moving_keys()
252 pressed_key
= event
.key()
254 if pressed_key
in selection_moving_keys
or pressed_key
== Qt
.Key_Escape
:
255 super().keyPressEvent(event
)
257 self
.switcher_inner_text
.emit(event
)
260 class SwitcherListItem(QtGui
.QStandardItem
):
261 """Item class for SwitcherTreeView and SwitcherSortFilterProxyModel"""
263 def __init__(self
, key
, icon
=None, name
=None):
264 QtGui
.QStandardItem
.__init
__(self
)