View Issue Details

IDProjectCategoryView StatusLast Update
0017143MMW 5Extensions frameworkpublic2023-05-18 11:05
Reporterdrakinite Assigned To 
PriorityurgentSeveritymajorReproducibilityN/A
Status closedResolutionreopened 
Target Version5.0Fixed in Version5.0 
Summary0017143: Add dedicated Addons panel in dlgOptions
DescriptionCurrently, 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.
TagsNo tags attached.
Attached Files
image.png (53,485 bytes)   
image.png (53,485 bytes)   
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);
    });
}
dlgOptions.js (7,670 bytes)   
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.js (579 bytes)   
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>
pnl_Addons.html (436 bytes)   
Fixed in build2278

Relationships

related to 0013903 feedbackjiri Extensions: MM5 Extension Tweaks 
related to 0013908 closedLudek Extensions: Addons Site browsable directly from MMW 
related to 0017283 closedLudek Addons: functionality is duplicated in 2 locations 

Activities

drakinite

2020-11-28 19:35

developer   ~0060434

Example of an addon that utilizes this premade Addons panel:
FPSCounter.mmip (27,303 bytes)

peke

2020-11-28 20:02

developer   ~0060435

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

jiri

2020-12-01 08:25

administrator   ~0060478

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').

Ludek

2020-12-01 17:51

developer   ~0060492

Last edited: 2020-12-01 20:49

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)

peke

2020-12-01 17:51

developer   ~0060493

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.

Ludek

2020-12-01 20:39

developer   ~0060497

Last edited: 2020-12-02 11:15

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.

drakinite

2020-12-02 21:29

developer   ~0060508

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

Ludek

2020-12-03 14:21

developer   ~0060530

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

peke

2020-12-19 19:12

developer   ~0060950

Verified Auto-DJ Config in 2288