66import functools
77from typing import (
88 Dict ,
9+ Generic ,
10+ Iterable ,
911 List ,
12+ TypeVar ,
1013)
14+ from typing_extensions import Protocol
1115
1216import wx
1317
18+ from _addonStore .models .status import _StatusFilterKey
1419from logHandler import log
20+ import ui
1521
16- from ..viewModels .action import AddonActionVM
22+ from ..viewModels .action import AddonActionVM , BulkAddonActionVM
23+ from ..viewModels .addonList import AddonListItemVM
1724from ..viewModels .store import AddonStoreVM
1825
1926
20- class _ActionsContextMenu :
21- def __init__ (self , storeVM : AddonStoreVM ):
22- self ._storeVM = storeVM
23- self ._actionMenuItemMap : Dict [AddonActionVM , wx .MenuItem ] = {}
24- self ._contextMenu = wx .Menu ()
27+ AddonActionT = TypeVar ("AddonActionT" , AddonActionVM , BulkAddonActionVM )
28+
29+
30+ class _ActionsContextMenuP (Generic [AddonActionT ], Protocol ):
31+ _actions : List [AddonActionT ]
32+ _actionMenuItemMap : Dict [AddonActionT , wx .MenuItem ]
33+ _contextMenu : wx .Menu
34+
35+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionT ):
36+ ...
2537
2638 def popupContextMenuFromPosition (
2739 self ,
@@ -31,14 +43,9 @@ def popupContextMenuFromPosition(
3143 self ._populateContextMenu ()
3244 targetWindow .PopupMenu (self ._contextMenu , pos = position )
3345
34- def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionVM ):
35- selectedAddon = actionVM .listItemVM
36- log .debug (f"action selected: actionVM: { actionVM } , selectedAddon: { selectedAddon } " )
37- actionVM .actionHandler (selectedAddon )
38-
3946 def _populateContextMenu (self ):
4047 prevActionIndex = - 1
41- for action in self ._storeVM . actionVMList :
48+ for action in self ._actions :
4249 menuItem = self ._actionMenuItemMap .get (action )
4350 menuItems : List [wx .MenuItem ] = list (self ._contextMenu .GetMenuItems ())
4451 isMenuItemInContextMenu = menuItem is not None and menuItem in menuItems
@@ -71,3 +78,68 @@ def _populateContextMenu(self):
7178 # Remove the menu item from the context menu.
7279 self ._contextMenu .RemoveItem (menuItem )
7380 del self ._actionMenuItemMap [action ]
81+
82+ menuItems : List [wx .MenuItem ] = list (self ._contextMenu .GetMenuItems ())
83+ for menuItem in menuItems :
84+ if menuItem not in self ._actionMenuItemMap .values ():
85+ # The menu item is not in the action menu item map.
86+ # It should be removed from the context menu.
87+ self ._contextMenu .RemoveItem (menuItem )
88+
89+
90+ class _MonoActionsContextMenu (_ActionsContextMenuP [AddonActionVM ]):
91+ """Context menu for actions for a single add-on"""
92+ def __init__ (self , storeVM : AddonStoreVM ):
93+ self ._storeVM = storeVM
94+ self ._actionMenuItemMap = {}
95+ self ._contextMenu = wx .Menu ()
96+
97+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : AddonActionVM ):
98+ selectedAddon = actionVM .listItemVM
99+ log .debug (f"action selected: actionVM: { actionVM .displayName } , selectedAddon: { selectedAddon } " )
100+ actionVM .actionHandler (selectedAddon )
101+
102+ @property
103+ def _actions (self ) -> List [AddonActionVM ]:
104+ return self ._storeVM .actionVMList
105+
106+
107+ class _BulkActionsContextMenu (_ActionsContextMenuP [BulkAddonActionVM ]):
108+ """Context menu for actions for a group of add-ons"""
109+ def __init__ (self , storeVM : AddonStoreVM ):
110+ self ._storeVM = storeVM
111+ self ._actionMenuItemMap = {}
112+ self ._contextMenu = wx .Menu ()
113+ self ._selectedAddons : Iterable [AddonListItemVM ] = tuple ()
114+
115+ def _updateSelectedAddons (self , selectedAddons : Iterable [AddonListItemVM ]):
116+ # Reset the action menu as self._actions depends on the selected add-ons
117+ self ._actionMenuItemMap = {}
118+ self ._selectedAddons = selectedAddons
119+
120+ def popupContextMenuFromPosition (
121+ self ,
122+ targetWindow : wx .Window ,
123+ position : wx .Position = wx .DefaultPosition
124+ ):
125+ super ().popupContextMenuFromPosition (targetWindow , position )
126+ if self ._contextMenu .GetMenuItemCount () == 0 :
127+ # Translators: a message displayed when activating the context menu on multiple selected add-ons,
128+ # but no actions are available for the add-ons.
129+ ui .message (pgettext ("addonStore" , "No actions available for the selected add-ons" ))
130+
131+ def _menuItemClicked (self , evt : wx .ContextMenuEvent , actionVM : BulkAddonActionVM ):
132+ log .debug (f"Performing bulk action for actionVM: { actionVM .displayName } " )
133+ actionVM .actionHandler (self ._selectedAddons )
134+
135+ @property
136+ def _actions (self ) -> List [BulkAddonActionVM ]:
137+ return [
138+ BulkAddonActionVM (
139+ # Translators: Label for an action that installs the selected add-ons
140+ displayName = pgettext ("addonStore" , "&Install selected add-ons" ),
141+ actionHandler = self ._storeVM .getAddons ,
142+ validCheck = lambda aVMs : self ._storeVM ._filteredStatusKey == _StatusFilterKey .AVAILABLE ,
143+ listItemVMs = self ._selectedAddons
144+ ),
145+ ]
0 commit comments