@@ -45,6 +45,7 @@ def _displayStringLabels(self) -> Dict["EnabledStatus", str]:
4545
4646
4747@enum .unique
48+ # TODO refactor rename from AvailableAddonStatus to AddonStatus
4849class AvailableAddonStatus (DisplayStringEnum ):
4950 """ Values to represent the status of add-ons within the NVDA add-on store.
5051 Although related, these are independent of the states in L{addonHandler}
@@ -53,6 +54,7 @@ class AvailableAddonStatus(DisplayStringEnum):
5354 PENDING_REMOVE = enum .auto ()
5455 AVAILABLE = enum .auto ()
5556 UPDATE = enum .auto ()
57+ UPDATE_INCOMPATIBLE = enum .auto ()
5658 REPLACE_SIDE_LOAD = enum .auto ()
5759 """
5860 Used when an addon in the store matches an installed add-on ID.
@@ -86,6 +88,8 @@ def _displayStringLabels(self) -> Dict["AvailableAddonStatus", str]:
8688 # Translators: Status for addons shown in the add-on store dialog
8789 self .UPDATE : pgettext ("addonStore" , "Update Available" ),
8890 # Translators: Status for addons shown in the add-on store dialog
91+ self .UPDATE_INCOMPATIBLE : pgettext ("addonStore" , "Update Available (incompatible)" ),
92+ # Translators: Status for addons shown in the add-on store dialog
8993 self .REPLACE_SIDE_LOAD : pgettext ("addonStore" , "Migrate to add-on store" ),
9094 # Translators: Status for addons shown in the add-on store dialog
9195 self .INCOMPATIBLE : pgettext ("addonStore" , "Incompatible" ),
@@ -147,6 +151,63 @@ class AddonStateCategory(str, enum.Enum):
147151 """Add-ons in this state are incompatible but their compatibility would be overridden on the next restart."""
148152
149153
154+ class _StatusFilterKey (DisplayStringEnum ):
155+ """Keys for filtering by status in the NVDA add-on store."""
156+ INSTALLED = enum .auto ()
157+ UPDATE = enum .auto ()
158+ AVAILABLE = enum .auto ()
159+ INCOMPATIBLE = enum .auto ()
160+
161+ @property
162+ def _displayStringLabels (self ) -> Dict ["_StatusFilterKey" , str ]:
163+ return {
164+ # Translators: The label of a tab to display installed add-ons in the add-on store.
165+ # Ensure the translation matches the label for the add-on list which includes an accelerator key.
166+ self .INSTALLED : pgettext ("addonStore" , "Installed add-ons" ),
167+ # Translators: The label of a tab to display updatable add-ons in the add-on store.
168+ # Ensure the translation matches the label for the add-on list which includes an accelerator key.
169+ self .UPDATE : pgettext ("addonStore" , "Updatable add-ons" ),
170+ # Translators: The label of a tab to display available add-ons in the add-on store.
171+ # Ensure the translation matches the label for the add-on list which includes an accelerator key.
172+ self .AVAILABLE : pgettext ("addonStore" , "Available add-ons" ),
173+ # Translators: The label of a tab to display incompatible add-ons in the add-on store.
174+ # Ensure the translation matches the label for the add-on list which includes an accelerator key.
175+ self .INCOMPATIBLE : pgettext ("addonStore" , "Installed incompatible add-ons" ),
176+ }
177+
178+ @property
179+ def _displayStringLabelsWithAccelerators (self ) -> Dict ["_StatusFilterKey" , str ]:
180+ return {
181+ # Translators: The label of the add-ons list in the corresponding panel.
182+ # Preferably use the same accelerator key for the four labels.
183+ # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
184+ self .INSTALLED : pgettext ("addonStore" , "Installed &add-ons" ),
185+ # Translators: The label of the add-ons list in the corresponding panel.
186+ # Preferably use the same accelerator key for the four labels.
187+ # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
188+ self .UPDATE : pgettext ("addonStore" , "Updatable &add-ons" ),
189+ # Translators: The label of the add-ons list in the corresponding panel.
190+ # Preferably use the same accelerator key for the four labels.
191+ # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
192+ self .AVAILABLE : pgettext ("addonStore" , "Available &add-ons" ),
193+ # Translators: The label of the add-ons list in the corresponding panel.
194+ # Preferably use the same accelerator key for the four labels.
195+ # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
196+ self .INCOMPATIBLE : pgettext ("addonStore" , "Installed incompatible &add-ons" ),
197+ }
198+
199+ @property
200+ def displayStringWithAccelerator (self ) -> str :
201+ """
202+ @return: The translated UI display string with accelerator that should be used for this value of the enum.
203+ """
204+ try :
205+ return self ._displayStringLabelsWithAccelerators [self ]
206+ except KeyError as e :
207+ log .error (f"No translation mapping for: { self } " )
208+ raise e
209+
210+
150211def _getDownloadableStatus (model : "_AddonGUIModel" ) -> Optional [AvailableAddonStatus ]:
151212 from ..dataManager import addonDataManager
152213 assert addonDataManager is not None
@@ -186,6 +247,8 @@ def _getUpdateStatus(model: "_AddonGUIModel") -> Optional[AvailableAddonStatus]:
186247 addonStoreInstalledData = addonDataManager ._getCachedInstalledAddonData (model .addonId )
187248 if addonStoreInstalledData is not None :
188249 if model .addonVersionNumber > addonStoreInstalledData .addonVersionNumber :
250+ if not model .isCompatible :
251+ return AvailableAddonStatus .UPDATE_INCOMPATIBLE
189252 return AvailableAddonStatus .UPDATE
190253 else :
191254 # Parsing from a side-loaded add-on
@@ -200,22 +263,18 @@ def _getUpdateStatus(model: "_AddonGUIModel") -> Optional[AvailableAddonStatus]:
200263 return AvailableAddonStatus .REPLACE_SIDE_LOAD
201264
202265 if model .addonVersionNumber > manifestAddonVersion :
266+ if not model .isCompatible :
267+ return AvailableAddonStatus .UPDATE_INCOMPATIBLE
203268 return AvailableAddonStatus .UPDATE
204269
205270 return None
206271
207272
208- def getStatus (model : "_AddonGUIModel" ) -> AvailableAddonStatus :
273+ def _getInstalledStatus (model : "_AddonGUIModel" ) -> Optional [ AvailableAddonStatus ] :
209274 from addonHandler import state as addonHandlerState
210-
211- downloadableStatus = _getDownloadableStatus (model )
212- if downloadableStatus :
213- # Is this available in the add-on store and not installed?
214- return downloadableStatus
215- else :
216- # Add-on is currently installed or pending restart
217- addonHandlerModel = model ._addonHandlerModel
218- assert addonHandlerModel
275+ from ..dataManager import addonDataManager
276+ assert addonDataManager is not None
277+ assert model ._addonHandlerModel is not None
219278
220279 for storeState , handlerStateCategories in _addonStoreStateToAddonHandlerState .items ():
221280 # Match special addonHandler states early for installed add-ons.
@@ -233,18 +292,41 @@ def getStatus(model: "_AddonGUIModel") -> AvailableAddonStatus:
233292 # and another for enabled/disabled.
234293 return storeState
235294
236- updateStatus = _getUpdateStatus (model )
237- if updateStatus :
238- # Can add-on be updated?
239- return updateStatus
240-
241- if addonHandlerModel .isRunning :
295+ if model ._addonHandlerModel .isRunning :
242296 return AvailableAddonStatus .RUNNING
243297
244- if addonHandlerModel .isEnabled :
298+ if model . _addonHandlerModel .isEnabled :
245299 return AvailableAddonStatus .ENABLED
246300
247- log .error (f"Add-on in unknown state: { model .addonId } " )
301+ return None
302+
303+
304+ def getStatus (model : "_AddonGUIModel" , context : _StatusFilterKey ) -> AvailableAddonStatus :
305+ """Get status for an add-on in the context of the current tab in the add-on store.
306+ e.g. "update available" from the update tab, and "installed (incompatible)" from the installed tab.
307+
308+ :param model: Add-on to determine the status of.
309+ :param context: Add-on Store tab context we are checking the status for.
310+ :return: Status of add-on for the context of the current tab.
311+ """
312+
313+ if context in (_StatusFilterKey .AVAILABLE , _StatusFilterKey .UPDATE ):
314+ downloadableStatus = _getDownloadableStatus (model )
315+ if downloadableStatus :
316+ # Is this available in the add-on store and not installed?
317+ return downloadableStatus
318+
319+ updateStatus = _getUpdateStatus (model )
320+ if updateStatus :
321+ # Can add-on be updated?
322+ return updateStatus
323+
324+ # This add-on should be installed if we aren't fetching for the available add-ons tab
325+ installedStatus = _getInstalledStatus (model )
326+ if installedStatus :
327+ return installedStatus
328+
329+ log .error (f"Add-on in unknown state: { model .addonId } { context } " )
248330 return AvailableAddonStatus .UNKNOWN
249331
250332
@@ -277,63 +359,6 @@ def getStatus(model: "_AddonGUIModel") -> AvailableAddonStatus:
277359})
278360
279361
280- class _StatusFilterKey (DisplayStringEnum ):
281- """Keys for filtering by status in the NVDA add-on store."""
282- INSTALLED = enum .auto ()
283- UPDATE = enum .auto ()
284- AVAILABLE = enum .auto ()
285- INCOMPATIBLE = enum .auto ()
286-
287- @property
288- def _displayStringLabels (self ) -> Dict ["_StatusFilterKey" , str ]:
289- return {
290- # Translators: The label of a tab to display installed add-ons in the add-on store.
291- # Ensure the translation matches the label for the add-on list which includes an accelerator key.
292- self .INSTALLED : pgettext ("addonStore" , "Installed add-ons" ),
293- # Translators: The label of a tab to display updatable add-ons in the add-on store.
294- # Ensure the translation matches the label for the add-on list which includes an accelerator key.
295- self .UPDATE : pgettext ("addonStore" , "Updatable add-ons" ),
296- # Translators: The label of a tab to display available add-ons in the add-on store.
297- # Ensure the translation matches the label for the add-on list which includes an accelerator key.
298- self .AVAILABLE : pgettext ("addonStore" , "Available add-ons" ),
299- # Translators: The label of a tab to display incompatible add-ons in the add-on store.
300- # Ensure the translation matches the label for the add-on list which includes an accelerator key.
301- self .INCOMPATIBLE : pgettext ("addonStore" , "Installed incompatible add-ons" ),
302- }
303-
304- @property
305- def _displayStringLabelsWithAccelerators (self ) -> Dict ["_StatusFilterKey" , str ]:
306- return {
307- # Translators: The label of the add-ons list in the corresponding panel.
308- # Preferably use the same accelerator key for the four labels.
309- # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
310- self .INSTALLED : pgettext ("addonStore" , "Installed &add-ons" ),
311- # Translators: The label of the add-ons list in the corresponding panel.
312- # Preferably use the same accelerator key for the four labels.
313- # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
314- self .UPDATE : pgettext ("addonStore" , "Updatable &add-ons" ),
315- # Translators: The label of the add-ons list in the corresponding panel.
316- # Preferably use the same accelerator key for the four labels.
317- # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
318- self .AVAILABLE : pgettext ("addonStore" , "Available &add-ons" ),
319- # Translators: The label of the add-ons list in the corresponding panel.
320- # Preferably use the same accelerator key for the four labels.
321- # Ensure the translation matches the label for the add-on tab which has the accelerator key removed.
322- self .INCOMPATIBLE : pgettext ("addonStore" , "Installed incompatible &add-ons" ),
323- }
324-
325- @property
326- def displayStringWithAccelerator (self ) -> str :
327- """
328- @return: The translated UI display string with accelerator that should be used for this value of the enum.
329- """
330- try :
331- return self ._displayStringLabelsWithAccelerators [self ]
332- except KeyError as e :
333- log .error (f"No translation mapping for: { self } " )
334- raise e
335-
336-
337362_installedAddonStatuses : Set [AvailableAddonStatus ] = {
338363 AvailableAddonStatus .UPDATE ,
339364 AvailableAddonStatus .REPLACE_SIDE_LOAD ,
@@ -355,12 +380,14 @@ def displayStringWithAccelerator(self) -> str:
355380 _StatusFilterKey .INSTALLED : _installedAddonStatuses ,
356381 _StatusFilterKey .UPDATE : {
357382 AvailableAddonStatus .UPDATE ,
383+ AvailableAddonStatus .UPDATE_INCOMPATIBLE ,
358384 AvailableAddonStatus .REPLACE_SIDE_LOAD ,
359385 },
360386 _StatusFilterKey .AVAILABLE : _installedAddonStatuses .union ({
361387 AvailableAddonStatus .INCOMPATIBLE ,
362388 AvailableAddonStatus .AVAILABLE ,
363389 AvailableAddonStatus .UPDATE ,
390+ AvailableAddonStatus .UPDATE_INCOMPATIBLE ,
364391 AvailableAddonStatus .REPLACE_SIDE_LOAD ,
365392 AvailableAddonStatus .DOWNLOAD_FAILED ,
366393 AvailableAddonStatus .DOWNLOAD_SUCCESS ,
0 commit comments