View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0017143 | MMW 5 | Extensions framework | public | 2020-11-28 19:23 | 2023-05-18 11:05 |
Reporter | drakinite | Assigned To | |||
Priority | urgent | Severity | major | Reproducibility | N/A |
Status | closed | Resolution | reopened | ||
Target Version | 5.0 | Fixed in Version | 5.0 | ||
Summary | 0017143: Add dedicated Addons panel in dlgOptions | ||||
Description | Currently, addons have no dedicated & unified place to put their options panels. It is possible for extension developers to create an "Addons" panel with their own extension's panel. But unless they're very careful with their code in dlgOptions_add.js, multiple extensions that attempt the same thing could cause errors, possibly even overwriting other extensions' panels. (example: extension1's dlgOptions_add.js: optionPanels.pnl_Addons = { name: _('Addons'), subPanels: { pnl_extension1: { name: 'Extension 1', order: 1 }}} extension2's dlgOptions_add.js: optionPanels.pnl_Addons = { name: _('Extensions'), subPanels: { pnl_extension1: { name: 'Extension 2', order: 1 }}} This would cause extension2 to overwrite the pnl_Addons that was added in extension1. ) It would be better for developers if pnl_Addons is already defined, and they can simply add their panel to the list: extension1's dlgOptions_add.js: optionPanels.pnl_Addons.subPanels.pnl_extension1 = { name: 'Extension 1', order: 1 } extension2's dlgOptions_add.js: optionPanels.pnl_Addons.subPanels.pnl_extension2 = { name: 'Extension 2', order: 1 } The pnl_Addons main panel can just include an info paragraph, a button to open the addons window, and a link to the addons download page. | ||||
Tags | No tags attached. | ||||
Attached Files | dlgOptions.js (7,670 bytes)
/* '(C) Ventis Media, Licensed under the Ventis Limited Reciprocal License - see: license.txt for details' */ "use strict"; requirejs("controls/listview"); requirejs("controls/gridview"); requirejs('controls/popupmenu'); // to show devtools and inspect element in debug mode var optionPanels = { pnl_General: { name: _('General'), subPanels: { pnl_Hotkeys: { name: _('Hotkeys'), order: 20 }, pnl_Confirmations: { name: _('Confirmations'), order: 30 }, pnl_PartyMode: { name: _('Party Mode'), order: 40 }, /* pnl_Network: { name: _('Network'), order: 50 },*/ pnl_Performance: { name: _('Performance'), order: 60 } }, order: 10 }, pnl_Player: { name: _('Player'), subPanels: { pnl_Streaming: { name: _('Streaming'), order: 10 }, pnl_AutoDJ: { name: _('Auto-DJ') + ' / ' + _('Playing list'), order: 20 }, pnl_VolumeLeveling: { name: _('Volume leveling'), order: 30 }, pnl_PlaybackRules: { name: _('Playback rules'), order: 40 }, pnl_InputPlugins: { name: _('Codecs') + ' / ' + _('Input') + ' (' + _('plug-ins') + ')', order: 50 }, pnl_OutputPlugins: { name: _('Audio Output') + ' (' + _('plug-ins') + ')', order: 60 }, pnl_DSPModules: { name: _('Audio DSP') + ' (' + _('plug-ins') + ')', order: 70 } }, order: 20 }, pnl_Layouts: { name: _('Layout'), order: 30, subPanels: { pnl_LayoutPlayer: { name: _('Player'), order: 10 }, pnl_LayoutPreview: { name: _('Preview'), order: 20 }, pnl_LayoutSkin: { name: _('Skin'), order: 30 }, } }, pnl_Library: { name: _('Library'), subPanels: { pnl_MediaTree: { name: _('Collections & Views'), order: 10 }, pnl_Appearance: { name: _('Fields'), order: 20 }, pnl_TagsAndPlaylists: { name: _('Tags & playlists'), order: 30 }, pnl_MetadataLookup: { name: _('Metadata lookup'), order: 40 }, pnl_AutoOrganize: { name: _('Auto-organize'), order: 50 }, pnl_Search: { name: _('Search'), order: 60 }, pnl_MediaServers: { name: _('Media Sharing'), order: 70 }, pnl_Download: { name: _('Downloads') + '/' + _('Podcasts'), order: 80 }, }, order: 40 }, pnl_Addons: { name: _('Addons'), subPanels: {}, order: 999 } }; if (!app.utils.getPortableMode()) optionPanels.pnl_General.subPanels.pnl_OSIntegration = { name: _('OS integration'), order: 10 }; function init(params) { var wnd = this; wnd.title = 'Options'; wnd.resizeable = true; var lvPanelList = qid('lvPanelList'); lvPanelList.controlClass.setContentBox(qid('right-box')); var addPanels = function (panelArray, parentKey, level) { level = level || 0; var panels = getSortedAsArray(panelArray); for (var iPnl = 0; iPnl < panels.length; iPnl++) { var panel = panels[iPnl]; if (parentKey) panel.parent = parentKey; panel.level = level; lvPanelList.controlClass.addPanel(panel); if (panel.subPanels) { addPanels(panel.subPanels, panel.key, level + 1); } } } addPanels(optionPanels); window.localListen(lvPanelList, 'loadpanel', function (e) { loadPanel(e.detail.panelID); }); lvPanelList.focus(); window.localListen(qid('btnOK'), 'click', btnOkClick); window.localListen(qid('btnCancel'), 'click', btnCancelClick, true); this.state = { selectedPanel: getFirstPanelKey() } app.getValue('dlg_options', this.state); var defaultPanel = this.state.selectedPanel; if (params && params.defaultPanel) defaultPanel = params.defaultPanel; selectPanel(defaultPanel); } function getFirstPanelKey() { for (var iPnl in optionPanels) { var panel = optionPanels[iPnl]; return panel.key; } } function getPanelByKey(panelID, panelArray) { panelArray = panelArray || optionPanels; for (var iPnl in panelArray) { var panel = panelArray[iPnl]; if (panel.key == panelID) return panel; if (panel.subPanels) { var retval = getPanelByKey(panelID, panel.subPanels); if (retval) return retval; } } } function selectPanel(panelID) { qid('lvPanelList').controlClass.selectPanel(panelID); if (getPanelByKey(panelID)) loadPanel(panelID); else { ODS(panelID + ' no longer exists!'); loadPanel(getFirstPanelKey()); } }; function loadPanel(panelID) { var panelsLV = qid('lvPanelList').controlClass; var pnl; if (!panelsLV.isPanelLoaded(panelID)) { var sheetsPath = 'file:///dialogs/dlgOptions/'; requirejs(sheetsPath + panelID + '.js'); var tempDiv = document.createElement('div'); tempDiv.innerHTML = window.loadFile(sheetsPath + panelID + '.html', undefined, { required: false }); pnl = tempDiv.firstElementChild; if (!pnl) pnl = tempDiv; pnl.setAttribute('data-id', panelID); qid('right-box').appendChild(pnl); initializeControls(pnl); var sett = window.settings.get(); getPanelByKey(panelID).load(sett, pnl); panelsLV.loadedPanels.push(panelID); } else { pnl = qid(panelID); setVisibility(pnl, true); } window.document.body.setAttribute('data-help', uitools.getHelpContext(pnl)); // set context help from panel window.state.selectedPanel = panelID; app.setValue('dlg_options', window.state); } function btnOkClick() { var sett = window.settings.get(); qid('lvPanelList').controlClass.forAllLoadedPanels(function (key) { getPanelByKey(key).save(sett); }); window.settings.set(sett); app.flushState(); if (window.reloadNeeded) modalResult = 2; else modalResult = 1; } function btnCancelClick() { var sett = window.settings.get(); qid('lvPanelList').controlClass.forAllLoadedPanels(function (key) { if (getPanelByKey(key).cancel) getPanelByKey(key).cancel(sett); }); } pnl_Addons.js (579 bytes)
/* '(C) Ventis Media, Licensed under the Ventis Limited Reciprocal License - see: license.txt for details' */ optionPanels.pnl_Addons.load = function (sett) { var btnOpenAddonsMenu = qid('btnOpenAddonsMenu') app.listen(btnOpenAddonsMenu, 'click', () => { window.uitools.showExtensions() }); var btnMoreAddons = qid('btnMoreAddons'); window.localListen(btnMoreAddons, 'click', function () { window.uitools.openWeb('http://www.mediamonkey.com/addons/browse/-mediamonkey-5-beta/'); }); } optionPanels.pnl_Addons.save = function (sett) { } pnl_Addons.html (436 bytes)
<div> <div class="hSeparatorTiny"> <table> <tr> <td data-icon="information" class="menuicon"></td> <td>If you have addons installed that have their own settings, they will appear here.</td> </tr> </table> <div data-control-class="Buttons"> <div data-id="btnOpenAddonsMenu" data-position='opposite'>Manage Addons...</div> <div data-id='btnMoreAddons' data-add-dots>Get more addons</div> </div> </div> | ||||
Fixed in build | 2278 | ||||
|
Example of an addon that utilizes this premade Addons panel: |
|
This is good idea as in MM4 it as very clear on how to add additional options panels in order to avoid these issues. MM5 on the other hand is more open and as pointed not fully handle this cases. Assigned to Jiri for triage. Improved Addons handling in MM is already handled and covered in designs at bugs 0013903 0013908 #13909 that are scheduled for 5.1 |
|
The idea here is that Addons mostly won't create a new category in the tree (i.e. Addons), but would rather include their config in the section that's most relevant to their functionality. That said, yes, for some Addons such a tree node would probably make sense. 1. We possibly could implement something like the proposed in order to create a base for addons config sheets. 2. We should add an add() method that adds a new config sheet without the need to assign it an id, so that the id is created automatically by MM5 and guaranteed to be unique (in order to prevent overwrite of panels, e.g. if two scripts name them 'subpanel1'). |
|
As discussed offline, some addons already have its own config (autoDJ sample, showLinks) and this config is accessible via the 'gear' button in the addons list. A) We should just make it more prominent, so there is an idea to add 'configurable' item to the dropdown here: https://www.dropbox.com/s/z3tfhih0y85amf1/Screenshot%202020-12-01%2018.39.13.png?dl=0 Add [CONFIGURE ADDONS...] button here https://www.dropbox.com/s/bcnt1yy3sxi065b/screenshot%202020-12-01%2021.49.01.png?dl=0 as shortcut to the adons list with 'configurable' filter. This should be an easy short term solution for 5.0 Futher bugs (with 5.0 target): B) currently the config is shown immediatelly after installing addon (e.g. showLinks addon), but this config isn't shown when addon is installed by double-clicking MMIP in file explorer. C) currently the optional configFile can be only JS file. It would be useful to support also HTML (like when adding dialog or sheet) |
|
Note: Proposed Adding additional Tree node is directly related to Online browsing of addons eg. 0013908 and making users easier way to access addons #13909 and eliminating/lowering need for Tools -> extensions. Not related discussion here. |
|
A) is implemented in 2278 B) fixed in 2278 C) support for optional config.html added in 2278 + demonstrated on the sampleScripts/showLinks addon (where I moved the html code from config.js to config.html) D) we could potentially add all addons configs as sub-panels of the Addons panel in Options, but I am not keen adding it automatically as e.g. autoDJ sample addon has its config already in Options > Player > Auto-DJ (when selected as source) so the settings would be redundant in Options. Therefore I think that [Configure addons...] button on the Addons tab in Options is enough for now. |
|
Oh! I didn't realize that Auto-DJ had its own "custom" panel in Options. Note that the Auto-DJ addon configuration panel is extremely simple (..and has a typo, lol). I think that the developer would want to choose whether to put their settings inside the "built-in" Addon Config panel (the one specified in info.json) or if they want to manually place their settings elsewhere in the options (via dlgOptions_add). So in the case of Auto-DJ, since it has its own panel under Options > Player > Auto-DJ, we should probably remove the "built-in" config (which only has a "Play counter threshold" option), and put the play counter threshold option (which is the only option inside the Addon Config panel) into Options > Player > Auto-DJ. Also, I forgot to mention this to you earlier, but there's a bug related to addon config that I reported the other day, which is a quick but important fix: 0017156 |
|
I agree that currently the AutoDJ script config is redundant, i.e. we can leave just the config in Player > Auto-DJ => Fixed in 2278 |
|
Verified Auto-DJ Config in 2288 |