Skip to content

Commit 5dd4033

Browse files
committed
script.skinshortcuts-3.0.0~beta7
1 parent 95829f2 commit 5dd4033

File tree

15 files changed

+876
-160
lines changed

15 files changed

+876
-160
lines changed

addon.xml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<addon id="script.skinshortcuts" name="Skin Shortcuts" version="3.0.0~beta6" provider-name="SiLVO, anxdpanic">
2+
<addon id="script.skinshortcuts" name="Skin Shortcuts" version="3.0.0~beta7" provider-name="SiLVO, anxdpanic">
33
<requires>
44
<import addon="xbmc.python" version="3.0.0"/>
5-
<import addon="script.module.simpleeval" version="0.9.10"/>
65
</requires>
76
<extension point="xbmc.python.script" library="default.py"/>
87
<extension point="xbmc.addon.metadata">

docs/codebase/INDEX.md

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Skin Shortcuts v3 - Codebase Documentation Index
22

33
**Version:** 3.0.0-dev
4-
**Total Files:** 35 Python source files
5-
**Total Lines:** ~9,500
64

75
**Quick Start:** See [OVERVIEW.md](OVERVIEW.md)
86

@@ -12,67 +10,68 @@
1210

1311
### Core Modules (`core/`)
1412

15-
| File | Doc | Lines | Purpose |
16-
|------|-----|-------|---------|
17-
| conditions.py | [conditions.md](core/conditions.md) | ~190 | Condition evaluation |
18-
| constants.py | [constants.md](core/constants.md) | ~85 | All constants |
19-
| exceptions.py | [exceptions.md](core/exceptions.md) | ~40 | Exception hierarchy |
20-
| localize.py | [localize.md](core/localize.md) | ~60 | Label localization |
21-
| hashing.py | [hashing.md](core/hashing.md) | ~150 | Rebuild detection |
22-
| userdata.py | [userdata.md](core/userdata.md) | ~260 | User data storage |
23-
| manager.py | [manager.md](core/manager.md) | ~500 | Menu manager API |
24-
| config.py | [config.md](core/config.md) | ~220 | Config loader |
25-
| entry.py | [entry.md](core/entry.md) | ~340 | Entry point |
13+
| File | Doc | Purpose |
14+
|------|-----|---------|
15+
| conditions.py | [conditions.md](core/conditions.md) | Condition evaluation |
16+
| expressions.py | [expressions.md](core/expressions.md) | $MATH and $IF expressions |
17+
| constants.py | [constants.md](core/constants.md) | All constants |
18+
| exceptions.py | [exceptions.md](core/exceptions.md) | Exception hierarchy |
19+
| localize.py | [localize.md](core/localize.md) | Label localization |
20+
| hashing.py | [hashing.md](core/hashing.md) | Rebuild detection |
21+
| userdata.py | [userdata.md](core/userdata.md) | User data storage |
22+
| manager.py | [manager.md](core/manager.md) | Menu manager API |
23+
| config.py | [config.md](core/config.md) | Config loader |
24+
| entry.py | [entry.md](core/entry.md) | Entry point |
2625

2726
### Dialog Package (`dialog/`)
2827

29-
| File | Doc | Lines | Purpose |
30-
|------|-----|-------|---------|
31-
| Overview | [README.md](dialog/README.md) | - | Package overview |
32-
| __init__.py | [init.md](dialog/init.md) | ~130 | Public API |
33-
| base.py | [base.md](dialog/base.md) | ~670 | Core initialization, events |
34-
| items.py | [items.md](dialog/items.md) | ~410 | Item operations |
35-
| pickers.py | [pickers.md](dialog/pickers.md) | ~520 | Shortcut/widget pickers |
36-
| properties.py | [properties.md](dialog/properties.md) | ~610 | Property management |
37-
| subdialogs.py | [subdialogs.md](dialog/subdialogs.md) | ~260 | Subdialog handling |
28+
| File | Doc | Purpose |
29+
|------|-----|---------|
30+
| Overview | [README.md](dialog/README.md) | Package overview |
31+
| `__init__.py` | [init.md](dialog/init.md) | Public API |
32+
| base.py | [base.md](dialog/base.md) | Core initialization, events |
33+
| items.py | [items.md](dialog/items.md) | Item operations |
34+
| pickers.py | [pickers.md](dialog/pickers.md) | Shortcut/widget pickers |
35+
| properties.py | [properties.md](dialog/properties.md) | Property management |
36+
| subdialogs.py | [subdialogs.md](dialog/subdialogs.md) | Subdialog handling |
3837

3938
### Models Package (`models/`)
4039

41-
| File | Doc | Lines | Purpose |
42-
|------|-----|-------|---------|
43-
| Overview | [README.md](models/README.md) | - | Package overview |
44-
| menu.py | [menu.md](models/menu.md) | ~370 | Menu/item models |
45-
| widget.py | [widget.md](models/widget.md) | ~80 | Widget models |
46-
| background.py | [background.md](models/background.md) | ~96 | Background models |
47-
| property.py | [property.md](models/property.md) | ~100 | Property schema models |
48-
| template.py | [template.md](models/template.md) | ~260 | Template models |
40+
| File | Doc | Purpose |
41+
|------|-----|---------|
42+
| Overview | [README.md](models/README.md) | Package overview |
43+
| menu.py | [menu.md](models/menu.md) | Menu/item models |
44+
| widget.py | [widget.md](models/widget.md) | Widget models |
45+
| background.py | [background.md](models/background.md) | Background models |
46+
| property.py | [property.md](models/property.md) | Property schema models |
47+
| template.py | [template.md](models/template.md) | Template models |
4948

5049
### Loaders Package (`loaders/`)
5150

52-
| File | Doc | Lines | Purpose |
53-
|------|-----|-------|---------|
54-
| Overview | [README.md](loaders/README.md) | - | Package overview |
55-
| base.py | [base.md](loaders/base.md) | ~150 | Base XML utilities |
56-
| menu.py | [menu.md](loaders/menu.md) | ~480 | Menu config loader |
57-
| widget.py | [widget.md](loaders/widget.md) | ~122 | Widget config loader |
58-
| background.py | [background.md](loaders/background.md) | ~162 | Background loader |
59-
| property.py | [property.md](loaders/property.md) | ~280 | Property schema loader |
60-
| template.py | [template.md](loaders/template.md) | ~480 | Template schema loader |
51+
| File | Doc | Purpose |
52+
|------|-----|---------|
53+
| Overview | [README.md](loaders/README.md) | Package overview |
54+
| base.py | [base.md](loaders/base.md) | Base XML utilities |
55+
| menu.py | [menu.md](loaders/menu.md) | Menu config loader |
56+
| widget.py | [widget.md](loaders/widget.md) | Widget config loader |
57+
| background.py | [background.md](loaders/background.md) | Background loader |
58+
| property.py | [property.md](loaders/property.md) | Property schema loader |
59+
| template.py | [template.md](loaders/template.md) | Template schema loader |
6160

6261
### Builders Package (`builders/`)
6362

64-
| File | Doc | Lines | Purpose |
65-
|------|-----|-------|---------|
66-
| Overview | [README.md](builders/README.md) | - | Package overview |
67-
| includes.py | [includes.md](builders/includes.md) | ~290 | Includes.xml builder |
68-
| template.py | [template.md](builders/template.md) | ~850 | Template processor |
63+
| File | Doc | Purpose |
64+
|------|-----|---------|
65+
| Overview | [README.md](builders/README.md) | Package overview |
66+
| includes.py | [includes.md](builders/includes.md) | Includes.xml builder |
67+
| template.py | [template.md](builders/template.md) | Template processor |
6968

7069
### Providers Package (`providers/`)
7170

72-
| File | Doc | Lines | Purpose |
73-
|------|-----|-------|---------|
74-
| Overview | [README.md](providers/README.md) | - | Package overview |
75-
| content.py | [content.md](providers/content.md) | ~500 | Dynamic content resolver |
71+
| File | Doc | Purpose |
72+
|------|-----|---------|
73+
| Overview | [README.md](providers/README.md) | Package overview |
74+
| content.py | [content.md](providers/content.md) | Dynamic content resolver |
7675

7776
***
7877

@@ -292,3 +291,4 @@ When adding tests to the codebase, these are the recommended priorities:
292291
| Add new exception | exceptions.py |
293292
| Add new constant | constants.py |
294293
| Evaluate conditions | conditions.py |
294+
| Evaluate $MATH/$IF | expressions.py |

docs/codebase/OVERVIEW.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ entry.py (RunScript entry point)
4343
| `userdata.py` | User customizations (JSON), merge with skin defaults |
4444
| `hashing.py` | Rebuild detection via config hashes |
4545
| `localize.py` | Label resolution ($LOCALIZE, $INFO, etc.) |
46+
| `conditions.py` | Condition evaluation (=, ~, AND, OR, NOT, EMPTY, IN) |
47+
| `expressions.py` | Dynamic expressions ($MATH, $IF) for templates |
4648
| `constants.py` | Shared constants (file names, target maps) |
4749
| `exceptions.py` | Custom exception hierarchy |
4850

docs/codebase/builders/template.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ The TemplateBuilder is the most complex part of the build system. It processes t
1616
| Pattern | Purpose |
1717
|---------|---------|
1818
| `_PROPERTY_PATTERN` | Matches `$PROPERTY[name]` |
19-
| `_EXP_PATTERN` | Matches `$EXP[name]` (expressions) |
19+
| `_EXP_PATTERN` | Matches `$EXP[name]` (template expressions) |
20+
| `_INCLUDE_PATTERN` | Matches `$INCLUDE[name]` |
21+
22+
Dynamic expressions (`$MATH`, `$IF`) are handled by `expressions.py`.
2023

2124
***
2225

@@ -286,9 +289,15 @@ Handle `<skinshortcuts include="..."/>` element replacements.
286289

287290
### `_substitute_text`(text, context, item, menu)
288291

289-
Substitute `$PROPERTY[...]` in text.
292+
Substitute dynamic expressions in text.
290293

291-
Checks context first, then item properties.
294+
**Processing order:**
295+
296+
1. `$MATH[...]` - Arithmetic expressions (via `expressions.py`)
297+
2. `$IF[...]` - Conditional expressions (via `expressions.py`)
298+
3. `$PROPERTY[...]` - Property substitution
299+
300+
Checks context first, then item properties for `$PROPERTY`.
292301

293302
***
294303

docs/codebase/core/conditions.md

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# conditions.py
22

33
**Path:** `resources/lib/skinshortcuts/conditions.py`
4-
**Lines:** ~225
54
**Purpose:** Condition evaluation utilities for property-based conditions.
65

76
***
@@ -14,26 +13,41 @@ Evaluates property conditions using a simple expression language. Used throughou
1413

1514
## Expression Language
1615

17-
| Syntax | Meaning | Example |
18-
|--------|---------|---------|
19-
| `prop` | Truthy (has value) | `widgetPath` |
20-
| `!prop` | Falsy (empty/not set) | `!suffix` |
21-
| `prop=value` | Equality | `widgetType=movies` |
22-
| `prop=` | Empty string check | `suffix=` |
23-
| `prop~value` | Contains | `widgetPath~library` |
24-
| `cond1 + cond2` | AND | `widgetType=movies + widgetPath~library` |
25-
| `cond1 \| cond2` | OR | `widgetType=movies \| widgetType=tvshows` |
26-
| `!cond` | NOT | `!widgetType=custom` or `!suffix` |
27-
| `[cond]` | Grouping | `![widgetType=movies \| widgetType=tvshows]` |
28-
| `prop=v1 \| v2 \| v3` | Compact OR | Expands to `prop=v1 \| prop=v2 \| prop=v3` |
29-
30-
**Note:** Negation (`!`) applies to the immediately following term, so `!a + b` evaluates as `(!a) AND b`, not `!(a + b)`. Use brackets for grouped negation: `![a + b]`.
16+
Supports both symbol and keyword forms for all operators.
17+
18+
### Comparison Operators
19+
20+
| Symbol | Keyword | Meaning | Example |
21+
|--------|---------|---------|---------|
22+
| *(none)* | - | Truthy (has value) | `widgetPath` |
23+
| `=` | `EQUALS` | Equality | `widgetType=movies` or `widgetType EQUALS movies` |
24+
| `=` (empty) | - | Empty string check | `suffix=` |
25+
| `~` | `CONTAINS` | Contains substring | `widgetPath~library` or `widgetPath CONTAINS library` |
26+
| - | `EMPTY` | Is empty/not set | `widgetPath EMPTY` |
27+
| - | `IN` | Value in list | `widgetType IN movies,episodes,tvshows` |
28+
29+
### Logical Operators
30+
31+
| Symbol | Keyword | Meaning | Example |
32+
|--------|---------|---------|---------|
33+
| `+` | `AND` | Logical AND | `cond1 + cond2` or `cond1 AND cond2` |
34+
| `\|` | `OR` | Logical OR | `cond1 \| cond2` or `cond1 OR cond2` |
35+
| `!` | `NOT` | Negation | `!cond` or `NOT cond` |
36+
| `[]` | - | Grouping | `![cond1 \| cond2]` |
37+
38+
### Compact OR
39+
40+
```text
41+
prop=v1 | v2 | v3 → prop=v1 | prop=v2 | prop=v3
42+
```
43+
44+
**Note:** Negation applies to the immediately following term. `!a + b` evaluates as `(!a) AND b`, not `!(a + b)`. Use brackets for grouped negation: `![a + b]`.
3145

3246
***
3347

3448
## Functions
3549

36-
### evaluate_condition(condition, properties) → bool (line 109)
50+
### evaluate_condition(condition, properties) → bool
3751

3852
Main entry point - evaluate a condition against property values.
3953

@@ -44,18 +58,22 @@ Main entry point - evaluate a condition against property values.
4458

4559
**Returns:** True if condition matches, False otherwise. Empty/None conditions return True.
4660

61+
**Process:**
62+
63+
1. Normalize keywords to symbols (`AND``+`, `OR``|`, etc.)
64+
2. Expand compact OR syntax
65+
3. Evaluate expanded condition recursively
66+
4767
**Used by:**
4868

49-
* `dialog/base.py` - Fallback evaluation
50-
* `dialog/properties.py` - Option filtering
51-
* `dialog/pickers.py` - Shortcut filtering
52-
* `dialog/subdialogs.py` - Onclose condition evaluation
69+
* `dialog/` modules - Option filtering, fallbacks
5370
* `builders/template.py` - Template conditionals
54-
* `loaders/__init__.py` - Re-exported for backwards compatibility
71+
* `expressions.py` - $IF expression evaluation
72+
* `loaders/__init__.py` - Re-exported for convenience
5573

5674
***
5775

58-
### expand_compact_or(condition) → str (line 21)
76+
### expand_compact_or(condition) → str
5977

6078
Expand compact OR syntax to full form.
6179

@@ -70,39 +88,38 @@ Expand compact OR syntax to full form.
7088

7189
## Internal Functions
7290

73-
### `_split_preserving_brackets(text, delimiter)` → list[str] (line 70)
91+
### `_normalize_keywords(condition)` → str
92+
93+
Convert keyword operators to symbol equivalents. Uses word boundaries to avoid replacing within values.
94+
95+
**Conversions:** `AND``+`, `OR``|`, `NOT``!`, `EQUALS``=`, `CONTAINS``~`
96+
97+
### `_split_preserving_brackets(text, delimiter)` → list[str]
7498

7599
Split text by delimiter but preserve content inside brackets.
76100

77-
### `_expand_or_segment(segment)` → str (line 95)
101+
### `_expand_or_segment(segment)` → str
78102

79-
Expand a single OR segment.
103+
Expand a single OR segment with property cascading.
80104

81-
### `_evaluate_expanded(condition, properties)` → bool (line 166)
105+
### `_evaluate_expanded(condition, properties)` → bool
82106

83-
Evaluate an expanded condition (handles AND, OR, NOT, grouping).
107+
Evaluate an expanded condition. Handles AND, OR, NOT, and grouping recursively.
84108

85-
### `_evaluate_single(condition, properties)` → bool (line 194)
109+
### `_evaluate_single(condition, properties)` → bool
86110

87111
Evaluate a single atomic condition:
88112

89113
* `property` → True if property has non-empty value (truthy)
90-
* `!property` → True if property is empty/not set (falsy)
114+
* `property EMPTY` → True if property is empty/not set
115+
* `property IN a,b,c` → True if property value is in list
91116
* `property=value` → True if property equals value
92117
* `property~value` → True if property contains value
93118

94119
***
95120

96-
## Test Candidates
97-
98-
1. `evaluate_condition()` with truthy check (`prop`)
99-
2. `evaluate_condition()` with falsy check (`!prop`)
100-
3. `evaluate_condition()` with equality (`=`)
101-
4. `evaluate_condition()` with contains (`~`)
102-
5. `evaluate_condition()` with AND (`+`)
103-
6. `evaluate_condition()` with OR (`|`)
104-
7. `evaluate_condition()` with NOT (`!prop=value`)
105-
8. `evaluate_condition()` with grouping (`[]`)
106-
9. `evaluate_condition()` negation in compounds (`!a + b`)
107-
10. `expand_compact_or()` expansion
108-
11. Edge cases: empty conditions, missing properties
121+
## Related Modules
122+
123+
* `expressions.py` - Uses `evaluate_condition()` for $IF expressions
124+
* `builders/template.py` - Uses for template/preset conditionals
125+
* `dialog/` modules - Uses for option filtering

0 commit comments

Comments
 (0)