Kodi Community Forum

Full Version: best practice for several customizable widgets per main-MenuItem beside Weather widge
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello

Its been a While since i touched Skincode, and im stuck currently, by trying to allow the User to set several widgets per main menu Item. + have the Option to choose a weather widget to.

Im Looking for a Simple (or at least a good) way to have several customizable Widgets per main-menuItem. + Weather (and PVR Widgets).

I knew its possible to set "single Widgets" per menu Item, which is also quiet easys. Did already some Witchets that way, but when it comes to more than one widget per menuItem i get stuck.
I did already have a look at the Skin "Andromeda" which work with "Templates" (template.xml) to provide the widgets. But even this way i dont see a way to provide the ability to choose a weather widget beside thows.

(If i dont get This wrong, all i would need from the shortcut script are 5 properties (but multple Times per MenuItem)
- Widgetpath propertie
- WidgetTarhet propertie
- sortorder propetie
- Thumbsize propertie
- WidgetLabel propertie
And in best case the ability to set them within the shortcut-script (skriptskinshortcuts.xml), might similar to how submenu Items get set.


<!-- 1st Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath)]
$INFO[Container(9000).ListItem.Property(widgetTarget)]
$INFO[Container(9000).ListItem.Property(widgetName)]
<!-- 2nd Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath.2)]
$INFO[Container(9000).ListItem.Property(widgetTarget.2)]
$INFO[Container(9000).ListItem.Property(widgetName.2)]
<!-- 3rd Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath.3)]
$INFO[Container(9000).ListItem.Property(widgetTarget.3)]
$INFO[Container(9000).ListItem.Property(widgetName.3)]

But as long i didnt miss it, thats not possible with Skinshortcuts)
--

So in short - whats best practice?
(2019-10-14, 02:58)TP.One Wrote: [ -> ]Hello

Its been a While since i touched Skincode, and im stuck currently, by trying to allow the User to set several widgets per main menu Item. + have the Option to choose a weather widget to.

Im Looking for a Simple (or at least a good) way to have several customizable Widgets per main-menuItem. + Weather (and PVR Widgets).

I knew its possible to set "single Widgets" per menu Item, which is also quiet easys. Did already some Witchets that way, but when it comes to more than one widget per menuItem i get stuck.
I did already have a look at the Skin "Andromeda" which work with "Templates" (template.xml) to provide the widgets. But even this way i dont see a way to provide the ability to choose a weather widget beside thows.

(If i dont get This wrong, all i would need from the shortcut script are 5 properties (but multple Times per MenuItem)
- Widgetpath propertie
- WidgetTarhet propertie
- sortorder propetie
- Thumbsize propertie
- WidgetLabel propertie
And in best case the ability to set them within the shortcut-script (skriptskinshortcuts.xml), might similar to how submenu Items get set.


<!-- 1st Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath)]
$INFO[Container(9000).ListItem.Property(widgetTarget)]
$INFO[Container(9000).ListItem.Property(widgetName)]
<!-- 2nd Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath.2)]
$INFO[Container(9000).ListItem.Property(widgetTarget.2)]
$INFO[Container(9000).ListItem.Property(widgetName.2)]
<!-- 3rd Widget -->
$INFO[Container(9000).ListItem.Property(widgetPath.3)]
$INFO[Container(9000).ListItem.Property(widgetTarget.3)]
$INFO[Container(9000).ListItem.Property(widgetName.3)]

But as long i didnt miss it, thats not possible with Skinshortcuts)
--

So in short - whats best practice?

@TP.One , take a look at the Amber code, I provide the user with up to 6 customizable widgets, both dynamic and static content (weather, system info, next aired, etc.). I do use templates. In my case, only one widget or at the most two widgets are shown at the same time. My latest code is always at http://github.com/bartolomesoriano/skin.amber.

I can’t say that my code is best practice, but it works well. Please let me know here or pm me if you have any questions. Thanks.

Regards,

Bart
Thanks, Did have a quick Look in to ur skincode, but got problems to understand what exactly is done there.

What exactly have to be defined within the Template.xml and what in the overrides.xml and at script-skinshortcuts.xml? Doesnt Look that easy. All which look "knewn"/"familar" are the Includes for the static Weather Items (Looks bit similar in my currently fixcoded Widget.)

where does all these Properties come from within templates.xml?

My Widgets Look currently like this:

<!-- Widget Movies -->
        <control type="grouplist" id="9002">
            <include>WidgetPositionAnimation</include>
            <orientation>vertical</orientation>
            <left>307</left>
            <top>270</top>
            <height>450</height>
            <scrolltime tween="cubic" easing="out">500</scrolltime>
            <itemgap>0</itemgap>
            <animation effect="fade" start="0" end="100" time="0" condition="ControlGroup(9002).HasFocus | Skin.HasSetting(LowerMenuBar) | Skin.HasSetting(BottomMenuBar)">conditional</animation>

            <visible>true</visible>
            
            <!--animation effect="slide" start="10,0" end="0,0" time="200">Visible</animation-->
            
            <!-- In Progress Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31261]"/>
                <param name="Widgetcontent_path" value="special://skin/extras/SkinPlaylists/movies/inprogress_movies.xsp"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9020"/>
                <param name="Widgetonup_id" value="9000"/>
                <param name="Widgetondown_id" value="9021"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9020).NumItems,0) | Container(9020).IsUpdating"/>
            </include>
            <!-- Recently Added Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31037] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="dateadded"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9021"/>
                <param name="Widgetonup_id" value="9020"/>
                <param name="Widgetondown_id" value="9022"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9021).NumItems,0) | Container(9021).IsUpdating"/> 
            </include>
            <!-- Random Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31038] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9022"/>
                <param name="Widgetonup_id" value="9021"/>
                <param name="Widgetondown_id" value="9023"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9022).NumItems,0) | Container(9022).IsUpdating"/>
            </include>
            <!-- Recently Watched Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31039] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="lastplayed"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9023"/>
                <param name="Widgetonup_id" value="9022"/>
                <param name="Widgetondown_id" value="9024"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9023).NumItems,0) | Container(9023).IsUpdating"/>
            </include>
            <!-- Unwatched Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[16101] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="special://skin/extras/SkinPlaylists/movies/unwatched_movies.xsp"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9024"/>
                <param name="Widgetonup_id" value="9023"/>
                <param name="Widgetondown_id" value="9000"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9024).NumItems,0) | Container(9024).IsUpdating"/>
            </include>
        
        </control>
Basicly a Grouplist which holds the different Widgets (Lists) with there Header Lables.

What exacly does the shortcut shript do if the Widgets get set that (the template.xml) way? Is the same List used for all main menu items and it is refilled whenever the focus position of the main menu changes, or are there also different lists with different ID's for every Widget?
(2019-10-14, 15:47)TP.One Wrote: [ -> ]Thanks, Did have a quick Look in to ur skincode, but got problems to understand what exactly is done there.

What exactly have to be defined within the Template.xml and what in the overrides.xml and at script-skinshortcuts.xml? Doesnt Look that easy. All which look "knewn"/"familar" are the Includes for the static Weather Items (Looks bit similar in my currently fixcoded Widget.)

where does all these Properties come from within templates.xml?

My Widgets Look currently like this:

<!-- Widget Movies -->
        <control type="grouplist" id="9002">
            <include>WidgetPositionAnimation</include>
            <orientation>vertical</orientation>
            <left>307</left>
            <top>270</top>
            <height>450</height>
            <scrolltime tween="cubic" easing="out">500</scrolltime>
            <itemgap>0</itemgap>
            <animation effect="fade" start="0" end="100" time="0" condition="ControlGroup(9002).HasFocus | Skin.HasSetting(LowerMenuBar) | Skin.HasSetting(BottomMenuBar)">conditional</animation>

            <visible>true</visible>
            
            <!--animation effect="slide" start="10,0" end="0,0" time="200">Visible</animation-->
            
            <!-- In Progress Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31261]"/>
                <param name="Widgetcontent_path" value="special://skin/extras/SkinPlaylists/movies/inprogress_movies.xsp"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9020"/>
                <param name="Widgetonup_id" value="9000"/>
                <param name="Widgetondown_id" value="9021"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9020).NumItems,0) | Container(9020).IsUpdating"/>
            </include>
            <!-- Recently Added Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31037] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="dateadded"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9021"/>
                <param name="Widgetonup_id" value="9020"/>
                <param name="Widgetondown_id" value="9022"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9021).NumItems,0) | Container(9021).IsUpdating"/> 
            </include>
            <!-- Random Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31038] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9022"/>
                <param name="Widgetonup_id" value="9021"/>
                <param name="Widgetondown_id" value="9023"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9022).NumItems,0) | Container(9022).IsUpdating"/>
            </include>
            <!-- Recently Watched Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[31039] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="videodb://movies/titles/"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="lastplayed"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9023"/>
                <param name="Widgetonup_id" value="9022"/>
                <param name="Widgetondown_id" value="9024"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9023).NumItems,0) | Container(9023).IsUpdating"/>
            </include>
            <!-- Unwatched Movies -->
            <include content="WidgetLayoutPoster">
                <param name="widget_header" value="$LOCALIZE[16101] $LOCALIZE[20342]"/>
                <param name="Widgetcontent_path" value="special://skin/extras/SkinPlaylists/movies/unwatched_movies.xsp"/>
                <param name="widgetcontent_target" value="videos"/>
                <param name="widgetcontent_sortby" value="random"/>
                <param name="widgetcontent_sortorder" value="descending"/>
                <param name="Widgetlist_id" value="9024"/>
                <param name="Widgetonup_id" value="9023"/>
                <param name="Widgetondown_id" value="9000"/>
                <param name="Widgetlist_height" value="450"/>
                <param name="Widgetlist_width" value="1300"/>
                <param name="Widgetlist_visible" value="Integer.IsGreater(Container(9024).NumItems,0) | Container(9024).IsUpdating"/>
            </include>
        
        </control>
Basicly a Grouplist which holds the different Widgets (Lists) with there Header Lables.

What exacly does the shortcut shript do if the Widgets get set that (the template.xml) way? Is the same List used for all main menu items and it is refilled whenever the focus position of the main menu changes, or are there also different lists with different ID's for every Widget?
@TP.One , the most important thing from using template.xml is that each widget will have a different ID, so you will get much better performance.  While it would take a very long post to explain everything, I will provide you some answers to your questions here.  I think it is a very good idea to study the wiki for script.skinshortcuts.

1. In the overrides.xml, you can set the default values for properties.  For example, in mine I set what values the user will be able to choose from (when script-skinshortcuts.xml is shown) for sort by, sort order and limit, as you can see from this excerpt:


<!-- Widget 1 Sort -->
    <property property="widgetSortBy" label="$LOCALIZE[568]">lastplayed</property>
    <property property="widgetSortBy" label="$LOCALIZE[369]">title</property>
    <property property="widgetSortBy" label="$LOCALIZE[36902]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),episodes)">tvshowtitle</property>
    <property property="widgetSortBy" label="$LOCALIZE[552]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),episodes)">date</property>
    <property property="widgetSortBy" label="$LOCALIZE[557]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),musicvideos) | String.IsEqual(Container(211).ListItem.Property(widgetType),artists) | String.IsEqual(Container(211).ListItem.Property(widgetType),albums) | String.IsEqual(Container(211).ListItem.Property(widgetType),songs)">artist</property>
    <property property="widgetSortBy" label="$LOCALIZE[558]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),musicvideos) | String.IsEqual(Container(211).ListItem.Property(widgetType),albums) | String.IsEqual(Container(211).ListItem.Property(widgetType),songs)">album</property>
    <property property="widgetSortBy" label="$LOCALIZE[554]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),songs)">tracknumber</property>
    <property property="widgetSortBy" label="$LOCALIZE[19029]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),pvr)">channelnumber</property>
    <property property="widgetSortBy" label="$LOCALIZE[345]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies) | String.IsEqual(Container(211).ListItem.Property(widgetType),tvshows) | String.IsEqual(Container(211).ListItem.Property(widgetType),musicvideos) | String.IsEqual(Container(211).ListItem.Property(widgetType),albums)">year</property>
    <property property="widgetSortBy" label="$LOCALIZE[570]" condition="!String.IsEqual(Container(211).ListItem.Property(widgetType),pvr)">dateAdded</property>
    <property property="widgetSortBy" label="$LOCALIZE[590]">random</property>
    <property property="widgetSortBy" label="$LOCALIZE[515]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies) | String.IsEqual(Container(211).ListItem.Property(widgetType),tvshows) | String.IsEqual(Container(211).ListItem.Property(widgetType),musicvideos) | String.IsEqual(Container(211).ListItem.Property(widgetType),albums) | String.IsEqual(Container(211).ListItem.Property(widgetType),songs)">genre</property>
    <property property="widgetSortBy" label="$LOCALIZE[567]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies) | String.IsEqual(Container(211).ListItem.Property(widgetType),tvshows) | String.IsEqual(Container(211).ListItem.Property(widgetType),musicvideos) | String.IsEqual(Container(211).ListItem.Property(widgetType),episodes)">playcount</property>
    <property property="widgetSortBy" label="$LOCALIZE[572]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies) | String.IsEqual(Container(211).ListItem.Property(widgetType),tvshows)">studio</property>
    <property property="widgetSortBy" label="$LOCALIZE[574]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies)">country</property>
    <property property="widgetSortBy" label="$LOCALIZE[563]" condition="String.IsEqual(Container(211).ListItem.Property(widgetType),movies) | String.IsEqual(Container(211).ListItem.Property(widgetType),tvshows) | String.IsEqual(Container(211).ListItem.Property(widgetType),episodes)">rating</property>
    <property property="widgetSortBy" label="$LOCALIZE[33067]" condition="String.IsEqual(Container(211).ListItem.Property(widgetTarget),video) | String.IsEqual(Container(211).ListItem.Property(widgetTarget),music)">userrating</property>
    <propertySettings property="widgetSortBy" buttonID="3003" title="Widget Sort Method" templateonly="true"/>

    <!-- Widget 1 Sort Direction -->
    <property property="widgetSortOrder">ascending</property>
    <property property="widgetSortOrder">descending</property>
    <propertySettings property="widgetSortOrder" buttonID="3004" title="Widget Sort Direction" templateonly="true"/>

    <!-- Widget 1 Limit -->
    <property property="widgetLimit" label="10 ">$NUMBER[10]</property>
    <property property="widgetLimit" label="25 ">$NUMBER[25]</property>
    <property property="widgetLimit" label="50 ">$NUMBER[50]</property>
    <property property="widgetLimit" label="100 ">$NUMBER[100]</property>
    <propertySettings property="widgetLimit" buttonID="3005" title="Widget Item Limit" templateonly="true"/>

As you can see from the code above, depending on what the widget type is (movies, episodes, artists, etc.), I provide different values for the sort by.  Container 211 which is referenced in the code is part of script-skinshortcuts.xml.  You will also see that I specify the property name (widgetSortBy, widgetSortOrder, etc.), the button id (again, from script-skinshortcuts.xml) and that the properties will only be for widgets defined in template.xml.

Also, you can specify default values for widget properties if they are not filled or selected by the user, as is shown here:


<!-- Widget fallback if empty -->
    <!-- Show Two Shelves together is false by default -->
    <propertyfallback property="widgetTwoShelves">false</propertyfallback>
    <!-- Widget type is set to none by default -->
    <propertyfallback property="widgetType">none</propertyfallback>
    <propertyfallback property="widgetType.2">none</propertyfallback>
    <propertyfallback property="widgetType.3">none</propertyfallback>
    <propertyfallback property="widgetType.4">none</propertyfallback>
    <propertyfallback property="widgetType.5">none</propertyfallback>
    <propertyfallback property="widgetType.6">none</propertyfallback>    
    <!-- Set widgetContent to static for non-dynamic widgets -->
    <propertyfallback property="widgetContent" attribute="widget" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent.2" attribute="widget.2" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent.3" attribute="widget.3" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent.4" attribute="widget.4" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent.5" attribute="widget.5" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent.6" attribute="widget.6" value="static">static</propertyfallback>
    <propertyfallback property="widgetContent">default</propertyfallback>
    <propertyfallback property="widgetContent.2">default</propertyfallback>
    <propertyfallback property="widgetContent.3">default</propertyfallback>
    <propertyfallback property="widgetContent.4">default</propertyfallback>
    <propertyfallback property="widgetContent.5">default</propertyfallback>
    <propertyfallback property="widgetContent.6">default</propertyfallback>

As you can see from the code above, I use "static" as the widgetContent property for static widgets, and "default" for dynamic content widgets.

The other very important thing you specify in overrides.xml, is what are the widgets the user will be able to select from.  For that, look at the <widget-groupings> code in my file.  You will be able to see that for each widget, you can specify an id, a label, an icon, a type, a target and the condition(s) under which to show this widget as an option to the user.  An example is:


<shortcut widget="RecentMovies" label="$LOCALIZE[31423]" icon="DefaultMusicRecentlyPlayed.png" widgetType="movies" widgetTarget="video" condition="Library.HasContent(movies)">special://skin/xsp/movies_recentlyplayed.xsp</shortcut>

That example is to show a "Recently Played Movies" widget (type is movies, target is video, condition if there are movies in the library), and the widget content comes from a skin playlist.

I will explain as best as I can what one of my dynamic content widget code does (from template.xml) in the next post.

Regards,

Bart
@TP.One , here is the code, from my template.xml, for the first dynamic content widget.  What this means is that, if the conditions are met (I will explain below), the skinshortcuts script will generate an include for the widget, which will appear in the script-skinshortcuts-includes.xml file, which is automatically generated by the script in your skin's code folder.  So that you can have an idea, here is what one such file looks like: https://1drv.ms/u/s!AlII29kkG6TFiNl1hU8X-Fz5MY1mIw


<other include="shelfgroup1">
        <!-- Widget conditions: a path is needed, single shelf, dynamic content -->
        <condition tag="property" attribute="name|widgetPath" />
        <condition tag="property" attribute="name|widgetTwoShelves">false</condition>
        <condition tag="property" attribute="name|widgetContent">default</condition>
        <!-- Common values -->
        <propertyGroup>widgetCommon</propertyGroup>
        <!-- Retrieve properties -->
        <property name="label1" tag="property" attribute="name|widgetName" />
        <property name="path1" tag="property" attribute="name|widgetPath" />
        <property name="target1" tag="property" attribute="name|widgetTarget" />
        <property name="type1" tag="property" attribute="name|widgetType" />
        <property name="widget1" tag="property" attribute="name|widget" />
        <property name="sortby1" tag="property" attribute="name|widgetSortBy" />
        <property name="sortorder1" tag="property" attribute="name|widgetSortOrder" />
        <property name="limit1" tag="property" attribute="name|widgetLimit" />
        <!-- If target is set to true for next widget navigate to next (fallback if empty set to false in overrides.xml) -->
        <property name="onup" tag="property" attribute="name|widgetType.2" value="none">false</property>
        <property name="onup">true</property>
        <!-- Top -->
        <property name="top" tag="property" attribute="name|widgetDouble" value="true">170</property>
        <property name="top">327</property>
        <!-- Height -->
        <property name="height" tag="property" attribute="name|widgetDouble" value="true">630</property>
        <property name="height">315</property>
        <!-- Background Height -->
        <property name="backgroundheight" tag="property" attribute="name|widgetDouble" value="true">690</property>
        <property name="backgroundheight">375</property>
        <!-- Content -->
        <property name="labelvisibility1" tag="property" attribute="name|widgetType" value="episodes">true</property>
        <property name="labelvisibility1">false</property>
        <property name="aspect1" tag="property" attribute="name|widgetType" value="musicvideos">scale</property>
        <property name="aspect1">keep</property>        
        <property name="isYear1">'true' if path1 in 'years' else 'false'</property>
        <property name="isPlaylist1">'true' if (path1 in 'videoplaylists' or path1 in 'musicplaylists')  else 'false'</property>
        <property name="isMusicGenre1">'true' if target1 in 'music' and path1 in 'genres' else 'false'</property>
        <property name="content1">'Shelf_GenericSquare' if (type1 in 'movies' or type1 in 'tvshows' or type1 in 'episodes') and (path1 in 'tags' or path1 in 'genres' or path1 in 'studios' or path1 in 'actors' or path1 in 'directors' or path1 in 'country' or path1 in 'years') else 'Shelf_GenericSquare' if (type1 in 'addons' or type1 in 'games' or type1 in 'musicvideos' or target1 in 'music' or target1 in 'pvr' or target1 in 'tvguide' or target1 in 'radioguide' or path1 in 'videoplaylists' or path1 in 'musicplaylists' or path1 in 'sources') else 'Shelf_GenericPoster'</property>
        <controls>
            <control type="group">
                <skinshortcuts>visibility</skinshortcuts>
                <include content="WidgetBackground">
                    <param name="id">1600$SKINSHORTCUTS[id]1</param>
                    <param name="label">$SKINSHORTCUTS[label1]</param>
                    <param name="top">$SKINSHORTCUTS[top]</param>
                    <param name="height">$SKINSHORTCUTS[backgroundheight]</param>
                    <param name="visible">String.IsEmpty(Window(home).Property(WidgetFocus$SKINSHORTCUTS[id]))</param>
                </include>
                <control type="panel" id="1600$SKINSHORTCUTS[id]1">
                    <top>$SKINSHORTCUTS[top]</top>
                    <left>32</left>
                    <right>0</right>
                    <height>$SKINSHORTCUTS[height]</height>
                    <onclick condition="System.HasAddon(script.playalbum) + String.IsEqual(ListItem.DBType,album)">RunScript(script.playalbum,albumid=$INFO[ListItem.DBID])</onclick>
                    <onclick condition="String.IsEqual(ListItem.DBType,season)">ActivateWindow(Videos,videodb://tvshows/titles/$INFO[ListItem.Property(TVShowID)]/$INFO[ListItem.Property(Season)]/?tvshowid=$INFO[ListItem.Property(TVShowID)])</onclick>
                    <onup condition="$SKINSHORTCUTS[onup]">SetProperty(WidgetFocus$SKINSHORTCUTS[id],2,home)</onup>
                    <onup condition="$SKINSHORTCUTS[onup]">SetFocus(1600$SKINSHORTCUTS[id]2)</onup>
                    <ondown>300</ondown>
                    <onleft condition="Skin.HasSetting(VerticalHome)">300</onleft>
                    <preloaditems>2</preloaditems>
                    <orientation>horizontal</orientation>
                    <include content="$PYTHON[$SKINSHORTCUTS[content1]]">
                        <param name="id1">1600$SKINSHORTCUTS[id]1</param>
                        <param name="isYear">$PYTHON[$SKINSHORTCUTS[isYear1]]</param>
                        <param name="isPlaylist">$PYTHON[$SKINSHORTCUTS[isPlaylist1]]</param>
                        <param name="isMusicGenre">$PYTHON[$SKINSHORTCUTS[isMusicGenre1]]</param>
                        <param name="labelvisibility">$SKINSHORTCUTS[labelvisibility1]</param>
                        <param name="aspect">$SKINSHORTCUTS[aspect1]</param>
                    </include>
                    <content>
                        <item>
                            <icon>DefaultAddonNone.png</icon>
                            <thumb>DefaultAddonNone.png</thumb>
                            <visible>String.IsEmpty(Container(1600$SKINSHORTCUTS[id]1).ListItem(1).Label)</visible>
                        </item>
                    </content>
                    <content sortby="$SKINSHORTCUTS[sortby1]" sortorder="$SKINSHORTCUTS[sortorder1]" limit="$SKINSHORTCUTS[limit1]" target="$SKINSHORTCUTS[target1]">$SKINSHORTCUTS[path1]</content>
                </control>
            </control>
        </controls>
    </other>
<!-- Property groups -->
    <propertyGroup name="widgetCommon">
        <!-- Retrieve id -->
        <property name="id" tag="mainmenuid" />
        <!-- Check if other widgets are set -->
        <property name="target1" tag="property" attribute="name|widgetTarget" value="false">false</property>
        <property name="target1">true</property>
        <property name="target2" tag="property" attribute="name|widgetTarget.2" value="false">false</property>
        <property name="target2">true</property>
        <property name="target3" tag="property" attribute="name|widgetTarget.3" value="false">false</property>
        <property name="target3">true</property>
        <property name="target4" tag="property" attribute="name|widgetTarget.4" value="false">false</property>
        <property name="target4">true</property>
        <property name="target5" tag="property" attribute="name|widgetTarget.5" value="false">false</property>
        <property name="target5">true</property>
        <property name="target6" tag="property" attribute="name|widgetTarget.6" value="false">false</property>
        <property name="target6">true</property>
    </propertyGroup>

So from the code above, it starts by providing what are the conditions under which the include will be generated.  In this case, three are needed:

1. The widget has to have a path (there needs to be content defined for the widget).  This will come from the property widgetPath.
2. It must be a single widget (I allow users to show the first two widgets together, this is controlled by the custom property widgetTwoShelves)
3. It must be a dynamic content widget, which comes from the porperty widgetContent having the value "default" (as opposed to "static" in my case for static content widgets).

All of the conditions have to be met for the include to be generated.

The properties (widgetPath, widgetContent, etc.) will be assigned by the script to each menu item, depending on what the user chooses for that menu item when script-skinshortcuts.xml is shown.  Again, I encourage you to study the script's wiki.

Next, I set some common values that all of the widgets share in my skin.  That is the <propertyGroup> widgetCommon, which is at the end of the file for me, and which I also put in the code excerpt above.

If you look at that above, I set the property "id" to be "mainmenuid", which tells the script to retrieve the specific id for the menu item when generating the include.  This is a crucial part to having unique id's for each widget.

I also set the "target" property (target1 for the first widget, target2 for the second, etc.) to be the widgetTarget property from the menu item, since I use that property for the navigation of my widgets.

Going back to the widget code, I set all the properties that I will be using from the menu item, or that I will be assigning specific values to.  For example, the property label1 will come from the widgetName property assigned to the menu item.  That way, in the rest of the template, I can use label1 to refer to the widgetName property from the menu item.
Other examples in that part of the code are "top" and "height", which I set to specific values depending on the "widgetDouble" custom property assigned to the menu item.  I allow the users to show double-row widgets as well as single-row widgets.

There are some more complex examples of properties in the code in the <!-- Content --> part, like "content1", which will decide which include to use for this widget, either "Shelf_GenericSquare", which shows square icons with labels below, or "Shelf_GenericPoster", which shows posters.  I say that this example is more complex because it involves a python code snippet (the if ....) to decide which include to use based on the values for the type1, path1 and target1 menu item properties.  Shelf_GenericSquare and Shelf_GenericPoster can be found in Includes_Shelf_Contents.xml in my skin's 1080i folder.

The <controls> part of the code has the actual controls that will be used for the widget (in the generated include), and here is where you reference all of the properties that were defined before the <controls> tag.

As you can see, I have a <group> that includes some controls from my "WidgetBackground" include (found in Includes_Shelf_Contents.xml).  If you notice, the parameter "id" that I am passing to that include is "1600$SKINSHORTCUTS[id]1", which is a way of saying that the id is 1600+the id of the menu item+1.  So, if the menu item's id is "1", the id passed to the include will be 160011, which will be unique.

The widget itself you can see is a <panel> control, again with a unique id.  You can reference any of the properties defined in the template by using $SKINSHORTCUTS[<name of the property>].  Again, here the id of the panel is 1600$SKINSHORTCTUTS[id]1.  

The content for the panel is defined in either the include Shelf_GenericSquare or Shelf_GenericPoster.  The line 

<include content="$PYTHON[$SKINSHORTCUTS[content1]]">

means that I am telling the script to evaluate the python snippet contained in the property "content1" to determine what is the correct include to use.  Following are parameters that are needed for the include, like the id of the panel, and whether this widget is a Years widget, or a Music Genres widget (so I can show the icons I want for those) or whether there should be a label below the icon in the widget.

The lines

<content>
        <item>
             <icon>DefaultAddonNone.png</icon>
             <thumb>DefaultAddonNone.png</thumb>
             <visible>String.IsEmpty(Container(1600$SKINSHORTCUTS[id]1).ListItem(1).Label)</visible>
       </item>
</content>
 

are a way (thanks @mr. V ) to show an icon that says there is no content in the case that the widget is empty (for example, you have an a watched movies widget, but you have not watched any movies yet).

And finally, the line

<content sortby="$SKINSHORTCUTS[sortby1]" sortorder="$SKINSHORTCUTS[sortorder1]" limit="$SKINSHORTCUTS[limit1]" target="$SKINSHORTCUTS[target1]">$SKINSHORTCUTS[path1]</content>

sets the content for the panel.  The content comes from the path1 property, sort by from the sortby1 property, and so on.

I understand that perhaps this is a lot of information at one time.  I hope it is useful to you.

Regards,

Bart
Thanks verry much!! for all the explanation.

Guess need some quiet hours to wrap my head around it.

Thanks!
for programmable widgets it is possible to link a widget to a node and make the node with  library.node.editor


 <include content="WidgetListimages" condition="!Skin.HasSetting(youtube-row1) + Skin.HasSetting(youtube-WidgetList-row1)+ !Skin.HasSetting(youtubewidget_1_Categories_Square)">
                            <param name="content_path" value="library://video/widgets/youtube/row1/row1.xml"/>
                            <param name="widget_header" value="row1"/>
                            <param name="widget_target" value="videos"/>
                            <param name="list_id" value="40100"/>
                            <visible>!Skin.HasSetting(youtube-row1)</visible>
                            </include>    
                            <include content="WidgetListimages" condition="!Skin.HasSetting(youtube-row1) + Skin.HasSetting(youtube-WidgetList-row1)+ Skin.HasSetting(youtubewidget_1_Categories_Square)">
                            <param name="content_path" value="library://video/widgets/youtube/row1/"/>
                            <param name="widget_header" value="row1"/>
                            <param name="widget_target" value="videos"/>
                            <param name="list_id" value="40101"/>
                            <visible>!Skin.HasSetting(youtube-row1)</visible>
                            </include>    
 only downside is the widget name can not be set do you would need to name them row or line

ImageImageImageImage
i use an common way with the help of
script.skinshortcuts to provide path as skin string
example
(without using rebuild/use template)

https://github.com/marduklev/skin.swan-a...rrides.xml

i am just unsure about weather widget,
(i didnt test the path and provided propertys, but shouldnt be hard)


and use script.embuaryhelper to have a select dialog for set the attributes
https://github.com/marduklev/skin.swan-a...idgets.xml

snippet example set target
	<control type="button" id="11032">
<description>Set target</description>
<onclick>SetProperty(Dialog.1.Label,$LOCALIZE[3])</onclick>
<onclick>SetProperty(Dialog.1.BuiltIn,Skin.SetString(UserWidget$PARAM[nr]_target,videos)||Skin.SetString(UserWidget$PARAM[nr]_target.label,$LOCALIZE[3]))</onclick>
<onclick>SetProperty(Dialog.2.Label,$LOCALIZE[2])</onclick>
<onclick>SetProperty(Dialog.2.BuiltIn,Skin.SetString(UserWidget$PARAM[nr]_target,music)||Skin.SetString(UserWidget$PARAM[nr]_target.label,$LOCALIZE[2]))</onclick>
<onclick>SetProperty(Dialog.3.Label,$LOCALIZE[1])</onclick>
<onclick>SetProperty(Dialog.3.BuiltIn,Skin.SetString(UserWidget$PARAM[nr]_target,pictures)||Skin.SetString(UserWidget$PARAM[nr]_target.label,$LOCALIZE[1]))</onclick>
<onclick>SetProperty(Dialog.4.Label,$LOCALIZE[29921])</onclick>
<onclick>SetProperty(Dialog.4.BuiltIn,Skin.Reset(UserWidget$PARAM[nr]_target)||Skin.SetString(UserWidget$PARAM[nr]_target.label,$LOCALIZE[29921]))</onclick>
<onclick>RunScript(script.embuary.helper,action=createselect,header=$LOCALIZE[467])</onclick>
<label>Content Type</label>
<label2>$INFO[Skin.String(UserWidget$PARAM[nr]_target.label)]</label2>
</control>



content for include template
<content target="$INFO[Skin.String(UserWidget$PARAM[nr]_target)]" limit="$INFO[Skin.String(UserWidget$PARAM[nr]_limit)]" sortorder="$INFO[Skin.String(UserWidget$PARAM[nr]_sortorder)]" sortby="$INFO[Skin.String(UserWidget$PARAM[nr]_sortby)]">$INFO[Skin.String(UserWidget$PARAM[nr]_path)]</content>	
</control>