diff --git a/addons/arsenal/XEH_PREP.hpp b/addons/arsenal/XEH_PREP.hpp index 0539c820b6..3b063d8ebe 100644 --- a/addons/arsenal/XEH_PREP.hpp +++ b/addons/arsenal/XEH_PREP.hpp @@ -21,6 +21,7 @@ PREP(buttonActionsPage); PREP(buttonCargo); PREP(buttonClearAll); PREP(buttonExport); +PREP(buttonFavorites); PREP(buttonHide); PREP(buttonImport); PREP(buttonLoadoutsDelete); @@ -55,6 +56,7 @@ PREP(onLoadoutsClose); PREP(onLoadoutsOpen); PREP(onMouseButtonDown); PREP(onMouseButtonUp); +PREP(onPanelDblClick); PREP(onSelChangedLeft); PREP(onSelChangedLoadouts); PREP(onSelChangedRight); diff --git a/addons/arsenal/defines.hpp b/addons/arsenal/defines.hpp index 073d0b3d43..cfc12df3d3 100644 --- a/addons/arsenal/defines.hpp +++ b/addons/arsenal/defines.hpp @@ -5,7 +5,7 @@ #define WIDTH_TOTAL (safezoneW - 2 * (93 * GRID_W)) #define WIDTH_GAP (WIDTH_TOTAL / 100) -#define WIDTH_SINGLE ((WIDTH_TOTAL - 6 * WIDTH_GAP) / 5) +#define WIDTH_SINGLE ((WIDTH_TOTAL - 7 * WIDTH_GAP) / 6) // IDDs #define IDD_MISSION 46 @@ -19,6 +19,9 @@ #define ASCENDING 0 #define DESCENDING 1 +// Favorites +#define FAVORITES_COLOR (GVAR(favoritesColor) + [1]) + // IDCs #define IDD_ace_arsenal 1127001 #define IDC_mouseArea 0 @@ -39,6 +42,7 @@ #define IDC_buttonLoadouts 1003 #define IDC_buttonExport 1004 #define IDC_buttonImport 1005 +#define IDC_buttonFavorites 1006 #define IDC_infoBox 11 #define IDC_infoBackground 1101 #define IDC_infoName 1102 diff --git a/addons/arsenal/functions/fnc_addListBoxItem.sqf b/addons/arsenal/functions/fnc_addListBoxItem.sqf index 80e7de0dfc..fb5d3f18d0 100644 --- a/addons/arsenal/functions/fnc_addListBoxItem.sqf +++ b/addons/arsenal/functions/fnc_addListBoxItem.sqf @@ -1,4 +1,5 @@ #include "script_component.hpp" +#include "..\defines.hpp" /* * Author: Dedmen, johnb43 * Add a listbox row. @@ -20,6 +21,26 @@ params ["_configCategory", "_className", "_ctrlPanel", ["_pictureEntryName", "picture", [""]]]; +private _skip = GVAR(favoritesOnly) && {!(_className in GVAR(currentItems))} && {!((toLower _className) in GVAR(favorites))}; +if (_skip) then { + switch (GVAR(currentLeftPanel)) do { + case IDC_buttonPrimaryWeapon: { + _skip = !(_className in (GVAR(currentItems) select IDX_CURR_PRIMARY_WEAPON_ITEMS)); + }; + case IDC_buttonHandgun: { + _skip = !(_className in (GVAR(currentItems) select IDX_CURR_HANDGUN_WEAPON_ITEMS)); + }; + case IDC_buttonSecondaryWeapon: { + _skip = !(_className in (GVAR(currentItems) select IDX_CURR_PRIMARY_WEAPON_ITEMS)); + }; + case IDC_buttonBinoculars: { + _skip = !(_className in (GVAR(currentItems) select IDX_CURR_BINO_ITEMS)); + }; + }; +}; + +if (_skip) exitWith {}; + // Sanitise key, as it's public; If not in cache, find info and cache it for later use ((uiNamespace getVariable QGVAR(addListBoxItemCache)) getOrDefaultCall [_configCategory + _className, { // Get classname (config case), display name, picture and DLC @@ -35,3 +56,8 @@ _ctrlPanel lbSetData [_lbAdd, _className]; _ctrlPanel lbSetPicture [_lbAdd, _itemPicture]; _ctrlPanel lbSetPictureRight [_lbAdd, ["", _modPicture] select GVAR(enableModIcons)]; _ctrlPanel lbSetTooltip [_lbAdd, format ["%1\n%2", _displayName, _className]]; + +if ((toLower _className) in GVAR(favorites)) then { + _ctrlPanel lbSetColor [_lbAdd, FAVORITES_COLOR]; + _ctrlPanel lbSetSelectColor [_lbAdd, FAVORITES_COLOR]; +}; diff --git a/addons/arsenal/functions/fnc_buttonFavorites.sqf b/addons/arsenal/functions/fnc_buttonFavorites.sqf new file mode 100644 index 0000000000..b748ccc7ae --- /dev/null +++ b/addons/arsenal/functions/fnc_buttonFavorites.sqf @@ -0,0 +1,32 @@ +#include "script_component.hpp" +#include "..\defines.hpp" +/* + * Author: LinkIsGrim + * Switches the arsenal between displaying all items and favorites + * + * Arguments: + * 0: Arsenal display + * 1: Button control + * + * Return Value: + * None + * + * Public: No +*/ + +params ["_display", "_control"]; + +private _firstRun = false; + +if (isNil QGVAR(favoritesOnly)) then { + GVAR(favoritesOnly) = GVAR(defaultToFavorites); + _firstRun = true; +} else { + GVAR(favoritesOnly) = !GVAR(favoritesOnly); +}; + +_control ctrlSetText format ["%1: %2", localize "STR_GEAR_ITEMS", localize (["str_word_all", "STR_3DEN_Favorite_textPlural"] select GVAR(favoritesOnly))]; + +if (_firstRun) exitWith {}; + +[false] call FUNC(refresh); diff --git a/addons/arsenal/functions/fnc_buttonImport.sqf b/addons/arsenal/functions/fnc_buttonImport.sqf index ac4f947cfa..8c5510c6ba 100644 --- a/addons/arsenal/functions/fnc_buttonImport.sqf +++ b/addons/arsenal/functions/fnc_buttonImport.sqf @@ -51,7 +51,7 @@ if (GVAR(shiftState) && {is3DEN}) then { [GVAR(center), _extendedLoadout] call CBA_fnc_setLoadout; // Update current item list and unique items - call FUNC(refresh); + [true] call FUNC(refresh); _extendedLoadout params ["_loadout", "_extendedInfo"]; diff --git a/addons/arsenal/functions/fnc_buttonLoadoutsLoad.sqf b/addons/arsenal/functions/fnc_buttonLoadoutsLoad.sqf index 99f7894311..c02fcc4b7d 100644 --- a/addons/arsenal/functions/fnc_buttonLoadoutsLoad.sqf +++ b/addons/arsenal/functions/fnc_buttonLoadoutsLoad.sqf @@ -38,7 +38,7 @@ private _extendedLoadout = switch (GVAR(currentLoadoutsTab)) do { [GVAR(center), _extendedLoadout, true] call CBA_fnc_setLoadout; // Update current item list and unique items -call FUNC(refresh); +[true] call FUNC(refresh); _extendedLoadout params ["_loadout", "_extendedInfo"]; diff --git a/addons/arsenal/functions/fnc_fillRightPanel.sqf b/addons/arsenal/functions/fnc_fillRightPanel.sqf index 12c7d9d32b..3be98c737e 100644 --- a/addons/arsenal/functions/fnc_fillRightPanel.sqf +++ b/addons/arsenal/functions/fnc_fillRightPanel.sqf @@ -43,9 +43,13 @@ private _cfgMagazines = configFile >> "CfgMagazines"; private _cfgWeapons = configFile >> "CfgWeapons"; private _rightPanelCache = uiNamespace getVariable QGVAR(rightPanelCache); +private _currentCargo = itemsWithMagazines GVAR(center); + private _fnc_fillRightContainer = { params ["_configCategory", "_className", "_hasItemInfo", ["_isUnique", false, [false]], ["_unknownOrigin", false, [false]]]; + if (GVAR(favoritesOnly) && {!(_className in _currentCargo)} && {!((toLower _className) in GVAR(favorites))}) exitWith {}; + // If item is not in the arsenal, it must be unique if (!_isUnique && {!(_className in GVAR(virtualItemsFlat))}) then { _isUnique = true; @@ -94,11 +98,16 @@ private _fnc_fillRightContainer = { }, true]) params ["_displayName", "_picture", "_mass"]; private _lbAdd = _ctrlPanel lnbAddRow ["", _displayName, "0"]; + _ctrlPanel lnbSetText [[_lbAdd, 1], _displayName]; _ctrlPanel lnbSetData [[_lbAdd, 0], _className]; _ctrlPanel lnbSetPicture [[_lbAdd, 0], _picture]; _ctrlPanel lnbSetValue [[_lbAdd, 0], _mass]; _ctrlPanel lnbSetValue [[_lbAdd, 2], [0, 1] select _isUnique]; _ctrlPanel lnbSetTooltip [[_lbAdd, 0], format ["%1\n%2", _displayName, _className]]; + if ((toLower _className) in GVAR(favorites)) then { + _ctrlPanel lnbSetColor [[_lbAdd, 1], FAVORITES_COLOR]; + _ctrlPanel lnbSetColorRight [[_lbAdd, 1], FAVORITES_COLOR]; + }; }; private _ctrlPanel = _display displayCtrl IDC_rightTabContent; diff --git a/addons/arsenal/functions/fnc_handleActions.sqf b/addons/arsenal/functions/fnc_handleActions.sqf index 757616e3a7..c3c8e09c7e 100644 --- a/addons/arsenal/functions/fnc_handleActions.sqf +++ b/addons/arsenal/functions/fnc_handleActions.sqf @@ -94,10 +94,9 @@ _actionsCurrentPageCtrl ctrlCommit 0; case ACTION_TYPE_BUTTON: { _actionButtonCtrl ctrlRemoveAllEventHandlers "ButtonClick"; _actionButtonCtrl ctrlAddEventHandler ["ButtonClick", { - if (is3DEN) exitWith {call FUNC(refresh)}; - + if (is3DEN) exitWith {[true] call FUNC(refresh)}; [{ - call FUNC(refresh); + [true] call FUNC(refresh); }] call CBA_fnc_execNextFrame; }]; _actionButtonCtrl ctrlAddEventHandler ["ButtonClick", _statement]; diff --git a/addons/arsenal/functions/fnc_onArsenalClose.sqf b/addons/arsenal/functions/fnc_onArsenalClose.sqf index 73851d4aa9..222ef8a9d9 100644 --- a/addons/arsenal/functions/fnc_onArsenalClose.sqf +++ b/addons/arsenal/functions/fnc_onArsenalClose.sqf @@ -127,6 +127,10 @@ GVAR(statsInfo) = nil; GVAR(showActions) = nil; GVAR(currentActionPage) = nil; +profileNamespace setVariable [QGVAR(favorites), GVAR(favorites)]; +GVAR(favoritesOnly) = nil; +GVAR(favorites) = nil; + GVAR(center) = nil; GVAR(centerNotPlayer) = nil; diff --git a/addons/arsenal/functions/fnc_onArsenalOpen.sqf b/addons/arsenal/functions/fnc_onArsenalOpen.sqf index 62cbfc844a..f3c499cef0 100644 --- a/addons/arsenal/functions/fnc_onArsenalOpen.sqf +++ b/addons/arsenal/functions/fnc_onArsenalOpen.sqf @@ -95,6 +95,10 @@ GVAR(currentActionPage) = 0; // Update current item list call FUNC(updateCurrentItemsList); +// Setup favorites button text and switch to default mode defined by setting +[_display, _display displayCtrl IDC_buttonFavorites] call FUNC(buttonFavorites); +GVAR(favorites) = profileNamespace getVariable [QGVAR(favorites), createHashMap]; + // This takes care of unique inventory items and unique equipment (arsenal doesn't have items/equipment whitelisted) call FUNC(updateUniqueItemsList); diff --git a/addons/arsenal/functions/fnc_onPanelDblClick.sqf b/addons/arsenal/functions/fnc_onPanelDblClick.sqf new file mode 100644 index 0000000000..d9ea3583be --- /dev/null +++ b/addons/arsenal/functions/fnc_onPanelDblClick.sqf @@ -0,0 +1,50 @@ +#include "script_component.hpp" +#include "..\defines.hpp" +#include "\a3\ui_f\hpp\defineResincl.inc" +/* + * Author: LinkIsGrim + * Add or remove item(s) to favorites when LShift is pressed + * + * Arguments: + * 0: Left panel control + * 1: Left panel selection + * + * Return Value: + * None + * + * Public: No +*/ +params ["_control", "_curSel"]; + +if !(GVAR(shiftState)) exitWith {}; + +if (GVAR(currentLeftPanel) in [IDC_buttonFace, IDC_buttonVoice, IDC_buttonInsigina]) exitWith {}; + +private _isLnB = (ctrlType _control) == CT_LISTNBOX; + +private _favorited = false; + +// Favorites/blacklist will always be lowercase to handle configCase changes +private _item = ""; +if (_isLnB) then { + _item = toLower (_control lnbData [_curSel, 0]); +} else { + _item = toLower (_control lbData _curSel); +}; + +if (_item in GVAR(favorites)) then { + GVAR(favorites) deleteAt _item; +} else { + GVAR(favorites) set [_item, nil]; + _favorited = true; +}; + +private _color = ([[1, 1, 1], GVAR(favoritesColor)] select _favorited) + [1]; + +if (_isLnB) then { + _control lnbSetColor [[_curSel, 1], _color]; + _control lnbSetColorRight [[_curSel, 1], _color]; +} else { + _control lbSetColor [_curSel, _color]; + _control lbSetSelectColor [_curSel, _color]; +}; diff --git a/addons/arsenal/functions/fnc_refresh.sqf b/addons/arsenal/functions/fnc_refresh.sqf index 4b22e72f04..45358a3ce6 100644 --- a/addons/arsenal/functions/fnc_refresh.sqf +++ b/addons/arsenal/functions/fnc_refresh.sqf @@ -4,17 +4,26 @@ * Author: Brett Mayson, johnb43 * Refreshes the arsenal to show external changes. * + * Arguments: + * 0: Update current and unique items lists (default: true) + * * Return Value: * None * - * Public: No + * Example: + * call ace_arsenal_fnc_refresh + * + * Public: Yes */ +params [["_updateItems", true, [true]]]; -// Update current item list -call FUNC(updateCurrentItemsList); +if (_updateItems) then { + // Update current item list + call FUNC(updateCurrentItemsList); -// This takes care of unique inventory items (arsenal doesn't have it whitelisted) -call FUNC(updateUniqueItemsList); + // This takes care of unique inventory items (arsenal doesn't have it whitelisted) + call FUNC(updateUniqueItemsList); +}; // Don't refresh left panel if in loadout tab if (!isNull findDisplay IDD_loadouts_display) exitWith {}; diff --git a/addons/arsenal/functions/fnc_updateRightPanel.sqf b/addons/arsenal/functions/fnc_updateRightPanel.sqf index 04e93445ae..f1ef077c92 100644 --- a/addons/arsenal/functions/fnc_updateRightPanel.sqf +++ b/addons/arsenal/functions/fnc_updateRightPanel.sqf @@ -22,15 +22,18 @@ private _rightPanelCache = uiNamespace getVariable [QGVAR(rightPanelCache), crea private _mass = -1; private _color = []; +private _alpha = 1; // Grey out items that are too big to fit in remaining space of the container for "_row" from 0 to (lnbSize _control select 0) - 1 do { _mass = _rightPanelCache getOrDefault [_control lnbData [_row, 0], 0]; + _color = _control lnbColor [_row, 1]; // Lower alpha on color for items that can't fit - _color = [1, 1, 1, [0.25, 1] select (_mass <= _loadRemaining)]; + _alpha = [0.25, 1] select (_mass <= _loadRemaining); + _color set [3, _alpha]; _control lnbSetColor [[_row, 1], _color]; - _control lnbSetColor [[_row, 2], _color]; + _control lnbSetColor [[_row, 2], [1, 1, 1, _alpha]]; }; private _display = ctrlParent _control; diff --git a/addons/arsenal/initSettings.sqf b/addons/arsenal/initSettings.sqf index ea549528ad..bc093eb522 100644 --- a/addons/arsenal/initSettings.sqf +++ b/addons/arsenal/initSettings.sqf @@ -33,10 +33,29 @@ private _category = LLSTRING(settingCategory); true ] call CBA_fnc_addSetting; +[ + QGVAR(defaultToFavorites), + "CHECKBOX", + [LSTRING(defaultToFavoritesSetting), LSTRING(defaultToFavoritesTooltip)], + _category, + false, + 2 // never overwrite the client +] call CBA_fnc_addSetting; + +[ + QGVAR(favoritesColor), + "COLOR", + [LSTRING(favoritesColorSetting), LSTRING(favoritesColorTooltip)], + _category, + [0.9, 0.875, 0.6], + 2 // never overwrite the client +] call CBA_fnc_addSetting; + private _loadoutCategory = LLSTRING(loadoutSubcategory); // Arsenal loadouts -[QGVAR(allowDefaultLoadouts), +[ + QGVAR(allowDefaultLoadouts), "CHECKBOX", [LSTRING(allowDefaultLoadoutsSetting), LSTRING(defaultLoadoutsTooltip)], [_category, _loadoutCategory], @@ -44,7 +63,8 @@ private _loadoutCategory = LLSTRING(loadoutSubcategory); true ] call CBA_fnc_addSetting; -[QGVAR(allowSharedLoadouts), +[ + QGVAR(allowSharedLoadouts), "CHECKBOX", LLSTRING(allowSharingSetting), [_category, _loadoutCategory], @@ -52,7 +72,8 @@ private _loadoutCategory = LLSTRING(loadoutSubcategory); true ] call CBA_fnc_addSetting; -[QGVAR(EnableRPTLog), +[ + QGVAR(EnableRPTLog), "CHECKBOX", [LSTRING(printToRPTSetting), LSTRING(printToRPTTooltip)], @@ -61,21 +82,24 @@ private _loadoutCategory = LLSTRING(loadoutSubcategory); false ] call CBA_fnc_addSetting; -[QGVAR(loadoutsSaveFace), +[ + QGVAR(loadoutsSaveFace), "CHECKBOX", LLSTRING(loadoutsSaveFaceSetting), [_category, _loadoutCategory], false ] call CBA_fnc_addSetting; -[QGVAR(loadoutsSaveVoice), +[ + QGVAR(loadoutsSaveVoice), "CHECKBOX", LLSTRING(loadoutsSaveVoiceSetting), [_category, _loadoutCategory], false ] call CBA_fnc_addSetting; -[QGVAR(loadoutsSaveInsignia), +[ + QGVAR(loadoutsSaveInsignia), "CHECKBOX", LLSTRING(loadoutsSaveInsigniaSetting), [_category, _loadoutCategory], diff --git a/addons/arsenal/stringtable.xml b/addons/arsenal/stringtable.xml index dbbed5fade..4c1dc4cffc 100644 --- a/addons/arsenal/stringtable.xml +++ b/addons/arsenal/stringtable.xml @@ -1572,5 +1572,20 @@ Ammo count + + Default to Favorites + + + Controls whether the ACE Arsenal defaults to showing all items or favorites. + + + Favorites Color + + + Highlight color for favorited items. + + + Switch between displaying all items or your favorites.\nDouble click while holding Shift to add or remove an item. + diff --git a/addons/arsenal/ui/RscAttributes.hpp b/addons/arsenal/ui/RscAttributes.hpp index 72b048f2c3..3a707a3dc3 100644 --- a/addons/arsenal/ui/RscAttributes.hpp +++ b/addons/arsenal/ui/RscAttributes.hpp @@ -176,6 +176,7 @@ class GVAR(display) { text = CSTRING(buttonHideText); sizeEx = QUOTE(5 * GRID_H); tooltip = CSTRING(buttonHideTooltip); + onMouseEnter = QUOTE(ctrlSetFocus (_this select 0)); onButtonClick = QUOTE([ctrlParent (_this select 0)] call FUNC(buttonHide)); }; class buttonLoadouts: buttonHide { @@ -199,10 +200,17 @@ class GVAR(display) { tooltip = CSTRING(buttonImportTooltip); onButtonClick = QUOTE([ctrlParent (_this select 0)] call FUNC(buttonImport)); }; + class buttonFavorites: buttonHide { + idc = IDC_buttonFavorites; + x = QUOTE(5 * WIDTH_GAP + 4 * WIDTH_SINGLE); + text = ""; + tooltip = CSTRING(buttonFavoritesTooltip); + onButtonClick = QUOTE([ARR_2(ctrlParent (_this select 0), (_this select 0))] call FUNC(buttonFavorites)); + }; class buttonClose: ctrlButtonOK { idc = IDC_menuBarClose; colorBackground[] = {0,0,0,0.8}; - x = QUOTE(5 * WIDTH_GAP + 4 * WIDTH_SINGLE); + x = QUOTE(6 * WIDTH_GAP + 5 * WIDTH_SINGLE); y = QUOTE(0); w = QUOTE(WIDTH_SINGLE); h = QUOTE(7 * GRID_H); @@ -559,6 +567,7 @@ class GVAR(display) { colorSelect2[] = {1,1,1,1}; colorPictureRightSelected[] = {1,1,1,1}; onLBSelChanged = QUOTE(_this call FUNC(onSelChangedLeft)); + onLBDblClick = QUOTE(_this call FUNC(onPanelDblClick)); onSetFocus = QUOTE(GVAR(leftTabFocus) = true); onKillFocus = QUOTE(GVAR(leftTabFocus) = false); x = QUOTE(safezoneX + 13 * GRID_W); @@ -572,6 +581,7 @@ class GVAR(display) { drawSideArrows = 1; disableOverflow = 1; onLBSelChanged = QUOTE(_this call FUNC(onSelChangedRight)); + onLBDblClick = QUOTE(_this call FUNC(onPanelDblClick)); onSetFocus = QUOTE(GVAR(rightTabFocus) = true); onKillFocus = QUOTE(GVAR(rightTabFocus) = false); x = QUOTE(safezoneX + safezoneW - 93 * GRID_W); @@ -592,6 +602,7 @@ class GVAR(display) { drawSideArrows = 1; disableOverflow = 1; onLBSelChanged = QUOTE(_this call FUNC(onSelChangedRightListnBox)); + onLBDblClick = QUOTE(_this call FUNC(onPanelDblClick)); onSetFocus = QUOTE(GVAR(rightTabLnBFocus) = true); onKillFocus = QUOTE(GVAR(rightTabLnBFocus) = false); x = QUOTE(safezoneX + safezoneW - 93 * GRID_W); diff --git a/docs/wiki/feature/arsenal.md b/docs/wiki/feature/arsenal.md index 5b1d8ee4d8..c1394e0230 100644 --- a/docs/wiki/feature/arsenal.md +++ b/docs/wiki/feature/arsenal.md @@ -29,6 +29,7 @@ ACE Arsenal has a pretty large number of improvements over BI Virtual Arsenal, h - An other setting to invert horizontal camera controls. - Settings to disable the "Default loadouts" and "Public loadouts" tabs. - Custom sub item categories for misc items +- Items can be saved as favorites to filter for your preferences. * Items not currently available in ACE Arsenal but in the unit's inventory, unique items will be omitted when loading loadouts and they can only be removed from containers. @@ -66,14 +67,26 @@ You can import loadouts from Virtual Arsenal into ACE Arsenal, face, voice, insi - In the 3DEN top toolbar, click on the `TOOLS` tab - Click on `Import BI VA Loadouts to Ace Arsenal` -## 2. Shortcuts +## 2. Favorites + +To switch between displaying all items or just favorites, press the `Items:` button on the bottom of the interface. + +Items can be saved to favorites by pressing Shift + 2x LMB. Favorites are saved to your profile, along with loadouts, and persist between missions and mod changes. + +Favorited items will be shown in a light gold color by default, but this can be changed under `ACE Arsenal -> Favorites Color` in CBA Settings. + +It is also possible to toggle between showing all items or favorites by default upon opening the arsenal via `ACE Arsenal -> Default to Favorites` in CBA Settings. + +Those settings cannot be overwritten by mission makers and are exclusively player preference. + +## 3. Shortcuts
Note:

Ctrl + V does NOT work in multiplayer due to a BI safety, however Ctrl + C does since it's using the ACE3 clipboard extension.

-### 2.1 Outside of search bars +### 3.1 Outside of search bars - Ctrl + C: Export current loadout to clipboard. - Ctrl + V: Import loadout from clipboard. @@ -81,13 +94,14 @@ You can import loadouts from Virtual Arsenal into ACE Arsenal, face, voice, insi - Arrow keys can be used to naviguate the left and right panels. - Shift + LMB on the `+` or `-` buttons on the right panel to add or remove 5 of the selected item. - Ctrl + F Selects the left search bar. +- Shift + 2x LMB on an item will add or remove it from favorites.
Note:

Shift + arrow keys can be used to add or remove a large amount of items in a short amount of time from the right panel.

-### 2.2 Inside of search bars +### 3.2 Inside of search bars - Search bars support copy / cut and paste (same limitations as importing apply in multiplayer). - Ctrl + A is supported.