6 Commits

Author SHA1 Message Date
HRiggs 30f3ab4ca5 versioning: 0.2.1
Build and Upload Release (Windows EXE) / Build Windows EXE (release) Successful in 35s
2026-04-23 12:29:01 -04:00
HRiggs c26ca6ec02 fix: include core 2026-04-23 12:27:56 -04:00
HRiggs 99d6d2a578 versioning: 0.2.0
Build and Upload Release (Windows EXE) / Build Windows EXE (release) Successful in 36s
2026-04-14 14:27:27 -04:00
HRiggs 603af4f378 fix: improve redraw speed 2026-04-14 14:26:10 -04:00
HRiggs 66e7ca4fc7 redesign: UI Work 2026-04-14 13:18:27 -04:00
HRiggs a015908d44 Steering fonts for UX overhaul 2026-01-25 14:23:54 -05:00
19 changed files with 3570 additions and 811 deletions
+10
View File
@@ -0,0 +1,10 @@
---
inclusion: always
---
<!------------------------------------------------------------------------------------
Add rules to this file or a short description and have Kiro refine them for you.
Learn about inclusion modes: https://kiro.dev/docs/steering/#inclusion-modes
------------------------------------------------------------------------------------->
This project has a GUI and a TUI when fixing or implmenting features access and make changes to both.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

+179
View File
@@ -0,0 +1,179 @@
# Steam Collections Page Design Update Summary
## Overview
Updated all UI screens (except mod manager) to match Steam Collections page design with modern dark blue theme, improved typography, and Steam-inspired visual elements.
## Color Scheme Changes
### New Steam Collections Inspired Colors
```python
COLORS = {
'bg_primary': '#1b2838', # Steam dark blue background
'bg_secondary': '#2a475e', # Steam medium blue background
'bg_tertiary': '#1e2328', # Steam darker background for cards
'bg_card': '#16202d', # Steam card background
'bg_hover': '#2a475e', # Steam hover state
'text_primary': '#c7d5e0', # Steam primary text (light blue-gray)
'text_highlight': '#66c0f4', # Steam blue highlight color
'text_body': '#8f98a0', # Steam body text (muted blue-gray)
'text_secondary': '#acb2b8', # Steam secondary text
'text_muted': '#67707b', # Steam muted text
'accent_green': '#5ba32b', # Steam success green
'accent_red': '#cd5c5c', # Steam error red
'accent_yellow': '#ffa500', # Steam warning orange
'accent_blue': '#66c0f4', # Steam signature blue
'border_light': '#3c4043', # Steam light border
'border_dark': '#0e141b', # Steam dark border
}
```
### Previous Colors (Replaced)
- Old yellow-green highlights (#f5f5b5) → Steam blue (#66c0f4)
- Old gray backgrounds (#424041) → Steam dark blue (#1b2838)
- Old bright colors → Muted Steam palette
## GUI Changes (steam_workshop_gui.py)
### 1. Main Application Window
- **Background**: Updated to Steam dark blue (#1b2838)
- **Card System**: Implemented Steam-style cards with rounded corners (12px radius)
- **Typography**: Enhanced with Steam blue highlights for titles
- **Padding**: Increased from 10px to 15px for better spacing
### 2. Input Section Redesign
- **Card-based Layout**: Each section now uses Steam-style cards
- RimWorld Installation card
- Workshop Content card
- Advanced Configuration card
- Steam Workshop Collections card
- **Input Fields**:
- Dark backgrounds (#1e2328)
- Steam blue focus borders (#66c0f4)
- Improved padding and spacing
- **Buttons**:
- Primary button: Steam blue (#66c0f4) with dark text
- Secondary button: Steam green (#5ba32b)
- Disabled state: Muted colors
### 3. Output Section (Logs)
- **Card Container**: Logs now in Steam-style card
- **Color-coded Messages**:
- Success: Steam green
- Error: Steam red
- Warning: Steam orange
- Info: Steam blue
- **Timestamps**: Added with muted color
- **Background**: Dark Steam theme (#1e2328)
### 4. Loading Screen Updates
- **Card Backgrounds**: Updated to Steam styling with subtle borders
- **Corner Radius**: Reduced from 20px to 12px for modern look
- **Colors**: All text and UI elements use Steam palette
- **Error Cards**: Steam red accents for error states
### 5. Button Styling
- **Enable State**: Steam blue primary, Steam green secondary
- **Disable State**: Muted Steam colors
- **Hover Effects**: Lighter Steam blue on hover
- **Cursor**: Hand cursor for better UX
## TUI Changes (progression_tui.py)
### 1. Color Scheme
- Updated all TUI_COLORS to match GUI Steam palette
- Consistent blue theme throughout terminal interface
### 2. Header Styling
- **Border**: Steam blue double border
- **Title**: Added "PROGRESSION LOADER" title to panel
- **ASCII Art**: Highlighted with Steam blue
### 3. Menu System
- **Table Styling**: Rounded borders with Steam blue accents
- **Status Indicators**:
- Valid: ✓ with Steam green
- Invalid: ⚠ with Steam yellow
- Ready: ✓ Ready with Steam green
- Disabled: ⚠ Disabled with muted colors
### 4. Configuration Display
- **Path Display**: Added icons and Steam blue labels
- **Panel Styling**: Rounded borders with Steam theme
- **Status Colors**: Consistent with GUI
## Font and Styling (font_and_colors_update.py)
### 1. New Utility Functions
- `apply_steam_styling()`: Returns Steam-inspired style dictionaries
- `get_steam_button_colors()`: Provides button color schemes by type
- Updated color constants to match Steam theme
### 2. Button Color Schemes
- **Default**: Steam secondary background
- **Primary**: Steam blue with dark text
- **Success**: Steam green
- **Warning**: Steam orange
- **Danger**: Steam red
## Visual Improvements
### 1. Card System
- **Rounded Corners**: 12px radius for modern look
- **Subtle Borders**: Light Steam borders (#3c4043)
- **Inner Highlights**: Very subtle Steam blue inner glow
- **Proper Spacing**: Consistent 10-15px padding
### 2. Typography Hierarchy
- **Titles**: Steam blue (#66c0f4) with bold weight
- **Labels**: Steam secondary text (#acb2b8)
- **Body Text**: Steam body color (#8f98a0)
- **Muted Text**: Steam muted (#67707b)
### 3. Interactive Elements
- **Focus States**: Steam blue borders on input fields
- **Hover Effects**: Lighter Steam blue on buttons
- **Status Indicators**: Color-coded with Steam palette
- **Blinking Animation**: Steam blue/muted for invalid states
## Consistency Improvements
### 1. Cross-Platform
- GUI and TUI now use matching color schemes
- Consistent terminology and styling
- Same visual hierarchy in both interfaces
### 2. Error Handling
- Consistent error colors (Steam red)
- Warning colors (Steam orange)
- Success colors (Steam green)
- Info colors (Steam blue)
### 3. Accessibility
- Better contrast ratios with Steam's tested palette
- Consistent focus indicators
- Clear visual hierarchy
## Files Modified
1. **steam_workshop_gui.py** - Main GUI application
2. **progression_tui.py** - Terminal user interface
3. **font_and_colors_update.py** - Color and styling utilities
4. **update_checker.py** - Update dialog styling
## Testing
- ✅ Syntax validation passed for all Python files
- ✅ Color scheme consistency verified
- ✅ Both GUI and TUI updated per requirements
- ✅ Steam Collections design patterns implemented
## Result
The application now features a cohesive Steam Collections inspired design with:
- Modern dark blue theme matching Steam's visual identity
- Improved card-based layouts for better organization
- Consistent typography and color usage
- Enhanced user experience with better visual hierarchy
- Professional appearance matching Steam's design standards
All screens except the mod manager have been updated to match the Steam Collections page design as requested.
+116
View File
@@ -0,0 +1,116 @@
@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@ @@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@ @@@@@@@@@@ @@@@@@@@@ @ @@@@@@@@@@
@....................@@@ @....................%@@@ @@@...........*@@@ @@@+................@@ @....................@@@ @@......................@@ @@@...................@@ @@@..................@@@ @@.......@@ @@@@...........@@@ @..@@ @@.......@@
@@.......................@@ @*......................@@@ @@..................@@@ @@@....................@@@@@@.......................@@ @@......................@@@@ @@%......................@@@ @@.......................@@@ @@.......@@@@ @@..................@@@ @....@@ @@.......@@@@
@@.........................@@@ @@#........................@@@ @@......................@@@ @@.......................@@@@@@.........................@@@ @@@......................@@@@@@@.........................@@@@ @.........................%@@@@@@.......@@@@@ @@......................@@@ @=.....@@ @@.......@@@@@
@@..........................@@@@@@*.........................@@@@ @@..........................@@@ @@........................@@@@@@..........................@@@@@@@......................@@@@@@..........................@@@@@@@..........................@@@@@@.......@@@@@@@..........................@@@@@@=......@@@ @@@.......@@@@@
@@.......@@@@@@@@@@@........@@@@@@*.......@@@@@@@@@@@........@@@@@@.........#@@@@@@@@..........@@@ @@..........@@@@@@@@@@@@@@@@@@@@@.......@@@@@@@@@@@........@@@@@@@.......@@@@@@@@@@@@@@@@@@@@@.......@@@@@@@@@@@@@@@@@@@@@@@@@........@@@@@@@@@@@@@@@@@@@@@@@@@.......@@@@@@..........@@@@@@@@@.........@@@@@=........@@@@@@.......@@@@@
@@.......@@@@@@@@@@@@.......@@@@@@#.......@@@@@@@@@@@........@@@@@........@@@@@@@@@@@@@.........@@@@@.........@@@@@@@@@@@@@@@@@@@@@@@.......@@@@@@@@@@@@.......@@@@@@@.......@@@@@@@@@@@@@@@@@@@@@.......@@@@@@@@@@@@@@@@@@@@@@@@@........@@@@@@@@@@@@@@@@@@@@@@@@@.......@@@@@@........@@@@@@@@@@@@@........@@@@=..........@@@@.......@@@@@
@@.......@@@@@@@@@@@@.......@@@@@@#.......@@@@@@@@@@@........@@@@@.......@@@@@@@@@@@@@@@@.......@@@@@.......@@@@@@@@@@@@@@@@@@@@@@@@@.......@@@@@@@@@@@@.......@@@@@@@................@@@@@@@@@@@@..................@@@@@@@@@@@@@@#..................@@@@@@@@@@@@@@.......@@@@@........@@@@@@@@@@@@@@@........@@@+............@@.......@@@@@
@@.......@.......:..........@@@@@@#.........................@@@@@........@@@@@@@@@@@@@@@@.......*@@@........@@@@@@@@@@@@@@@@@@.@@@@@@..........................@@@@@@@................@@@@@@@@@@@@@.....................@@@@@@@@@@@......................@@@@@@@@@@.......@@@@@.......@@@@@@@@@@@@@@@@@.......@@@+.....................@@@@@
@@.........................@@@@@@@#........................:@@@@@........@@@@@@@ @@@@@........@@@........@@@@@@@@ @@@.....@@@@@@.........................@@@@@@@@................@@@@@ @@@@......................@@@@ @@@@@......................@@@ @@@.......@@@@@.......@@@@@@@@ @@@@@.......@@@+.....................@@@@@
@@........................@@@@@@@@#.......................@@@@@@@@.......@@@@@@ @@@.......@@@@*.......@@@@@@ @........@@@@@@........................@@@@@@@@@................@@@@@ @@@@@@@....................@@@@ @@@@@@@.....................@@@@@@@.......@@@@@.......@@@@@@ @@@........@@@+.......@.............@@@@@
@@......................@@@@@@@@@@#.....................@@@@@@@@@@........@@@@ @@........@@@@@........@@@@ @........@@@@@@.....................@@@@@@@@@@@@.......@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@........@@@@@@@@@@@@@@@@@@@@@@@@........@@@@@@@.......@@@@@........@@@@@ @@.......@@@@*.......@@............@@@@@
@@.......@*********@@@@@@@@@@@@@@@#.......@****@........@@@@@@@@@@@........@@@@ @@@........@@@@@@.........@@@@ @@........@@@@@@.......@****%.........@@@@@@@@@@@.......@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@.......@@@@@ @@@@@@@@@@@@@@@@@@@........@@@@@@.......@@@@@@........@@@@ @@.........@@@@*.......@@@@..........@@@@@
@@.......@@@@@@@@@@@@@@@@@@@@@@@@@%.......@@@@@@@........@@@@@@@@@@...........@@@@@@..........@@@@@@@@............@@@@@........@@@@@@.......@@@@@@@.........@@@@@@@@@@.......@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@........@@@@@@@@@@@@@@@@@@@@@@@@........@@@@@@@.......@@@@@@@..........@@@@@@...........@@@@@*.......@@@@@@........@@@@@
@@.......@@@@@@@@@@@@@@@@@@@@ @@#.......@@@@@@@@........@@@@@@@@@@@........................@@@@@@@@@@@.......................@@@@@@.......@@@@@@@@=........@@@@ @@@......................@@@ @@.........................@@@@@@@..........................@@@@@@@.......@@@@@@@@........................@@@@@@@*.......@@@@@@@@......@@@@@
@@.......@@@@@@@@@@@@@@@@@ @@@.......@@@@@@@@@........@@@@@@@@@@@@....................@@@@@@@@@@@@@@-.....................@@@@@@.......@@@@@@@@@@........@@@@ @@@......................@@@@@@........................@@@@@@@@.........................@@@@@@@@.......@@@@@@@@@@.....................@@@@@@@@@.......@@@@@@@@@@....@@@@@
@@@......@@@@@@@@@@@@ @@@*......@@@@@@@@@@.........@@@@@@@@@@@@...............-@@@@@@@@@@@@@@@@@@@...................@@@@@@@......@@@@@@@@@@@........@@@@@@@@.....................@@@@@@@.....................@@@@@@@@@@@:.....................@@@@@@@@@@@......@@@@@@@@@@@@@...............@@@@@@@@@@@@@......@@@@@@@@@@@@..@@@@@
@@@@@@@.+@@@@@@ @@@@@@@-.@@@@@@@@@@@@@@@@@@@#@@@@@@@@@@@@@@@@@@#..@@@@@@@@@@@@@@@@ @@@@@@@@@@@@-...........-@@@@@@@@@@@.+@@@@@@@@@@@@@@@@@@@*@@@@@@@@@@#................*@@@@@@@@@@+............@@@@@@@@@@@@@@@@@@@@............%@@@@@@@@@@@@@@@@@@@*.@@@@@@@@@@@@@@@@@@@@:.*@@@@@@@@@@@@@@@@@@@@@@#.@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@
@@@@@@@@@@@@@ @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@ @@@@@@@@@ @@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@ @@@@@@@@@@@@@@@@@@ @@@@@@@@@ @@@@@
@@@@@@@@%@@@@@@@@
@@@....@@@@.....@@@@@@
%@@.....@@@@..........@@@@
%@@......@@@@.............@@@%
:@@.......@@@@...............@@@-
@@@.......@@@@.................@@@
@@@........@@@@..................@@@
@@@.....@@@@@@@@@@@.................@@+
@@@......@@-. .+@@@@:..............@@-
@@@.......@@ @@@@.............@@
+@@........@@@@@@@@@@@ @@@............@@%
-@@.........@@-......@@@@ =@@............@@
@@:.........@@.........=@@. @@@...........@@@
@@@..........@@..........:@@ .@@...........#@@
@@@...........@@...........@@ @@............@@
@@@............@@...........@@ @@...........=@@
#@@.............@@..........@@@ :@@...........@@@
*@@..............@@.........@@@ @@:...........@@=
:@@...............@@@@....@@@@: @@@............@@
@@:...............@@#@@@@@@+ #@@%............@@*
@@@................@@ .@@@@.............@@%
@@@.............@@@@@@@@@@@@@@@@@@..............@@@
@@@..............@@ @@.-@@@@...................@@@
@@@...............@@ @@........................@@@
=@@................@@ @@......................@@@.
@@.................@@ @@....................@@@#
@@@.................@@ @@..................@@@%
@@-..................@@ @@..............@@@@@
-@@@@@@@@@@@@@@@@@@@@@@@ @@.......*@@@@@@@*
+@@@@@@@@@@@@@@@@@@@@@ %@@@@@@@@@@%=
█████████████████
███░░░░████░░░░░██████
███░░░░░████░░░░░░░░░░████
███░░░░░░████░░░░░░░░░░░░░████
▒██░░░░░░░████░░░░░░░░░░░░░░░███▒
███░░░░░░░████░░░░░░░░░░░░░░░░░███
███░░░░░░░░████░░░░░░░░░░░░░░░░░░███
███░░░░░███████████░░░░░░░░░░░░░░░░░██▓
███░░░░░░██▒░ ░▓████▒░░░░░░░░░░░░░░██▒
███░░░░░░░██ ████░░░░░░░░░░░░░██
▓██░░░░░░░░███████████ ███░░░░░░░░░░░░███
▒██░░░░░░░░░██▒░░░░░░████ ▓██░░░░░░░░░░░░██
██▒░░░░░░░░░██░░░░░░░░░▒██░ ███░░░░░░░░░░░███
███░░░░░░░░░░██░░░░░░░░░░▒██ ░██░░░░░░░░░░░▓██
███░░░░░░░░░░░██░░░░░░░░░░░██ ██░░░░░░░░░░░░██
███░░░░░░░░░░░░██░░░░░░░░░░░██ ██░░░░░░░░░░░▓██
███░░░░░░░░░░░░░██░░░░░░░░░░███ ▒██░░░░░░░░░░░███
▓██░░░░░░░░░░░░░░██░░░░░░░░░███ ██▒░░░░░░░░░░░██▓
▒██░░░░░░░░░░░░░░░████░░░░████▒ ███░░░░░░░░░░░░██
██▒░░░░░░░░░░░░░░░█████████▓ ████░░░░░░░░░░░░██▓
███░░░░░░░░░░░░░░░░██ ░████░░░░░░░░░░░░░███
███░░░░░░░░░░░░░██████████████████░░░░░░░░░░░░░░███
███░░░░░░░░░░░░░░██ ██░▒████░░░░░░░░░░░░░░░░░░░███
███░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░░░░░░░░░░░███
▒██░░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░░░░░░░░░███░
██░░░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░░░░░░░████
███░░░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░░░░░████
██▒░░░░░░░░░░░░░░░░░░██ ██░░░░░░░░░░░░░░█████
▒███████████████████████ ██░░░░░░░▓███████▓
▓█████████████████████ ████████████▒
``````` ````````````````````````````````````````´
``````` ``````````````````````````````````````````
``````´ ´``````````````````````````````````````````
``````` ``````````````````````````````````````````´
``````` ````````
``````` ``````
``````` ´`````´
``````` ´``````
``````` ``````´
``````` ´```````
``````´````````````````````````` ```````````````````````````````
`````````````````````````````````` ´```````````````````````````
```````````````````````````````````` ´``````````````````````´
`````````````````````````````````````` ```````````````````
``````` `````` `````````´
``````` `````` ``````````
``````` `````` `````````
``````` `````` ``````````
``````` `````` ``````````
``````` `````` ``````````
``````` `````` ``````````
``````` `````` `````````´
``````` `````` `````````
``````` `````` ´`````````
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+187
View File
@@ -0,0 +1,187 @@
# Updated font loading and color scheme for Progression Loader
# Steam Collections page inspired design:
# - Dark blue theme matching Steam's signature colors
# - Steam blue (#66c0f4) for highlights and accents
# - Dark blue backgrounds (#1b2838, #2a475e) for depth
# - Light blue-gray text (#c7d5e0) for readability
# - Card-based layouts with rounded corners
# - Subtle shadows and glows for depth
import os
import sys
from pathlib import Path
def get_resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller"""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# Steam Collections inspired color constants
COLORS = {
'bg_primary': '#1b2838', # Steam dark blue background
'bg_secondary': '#2a475e', # Steam medium blue background
'bg_tertiary': '#1e2328', # Steam darker background for cards
'bg_card': '#16202d', # Steam card background
'bg_hover': '#2a475e', # Steam hover state
'text_primary': '#c7d5e0', # Steam primary text (light blue-gray)
'text_highlight': '#66c0f4', # Steam blue highlight color
'text_body': '#8f98a0', # Steam body text (muted blue-gray)
'text_secondary': '#acb2b8', # Steam secondary text
'text_muted': '#67707b', # Steam muted text
'accent_green': '#5ba32b', # Steam success green
'accent_red': '#cd5c5c', # Steam error red
'accent_yellow': '#ffa500', # Steam warning orange
'accent_blue': '#66c0f4', # Steam signature blue
'border_light': '#3c4043', # Steam light border
'border_dark': '#0e141b', # Steam dark border
}
def load_all_georgia_fonts():
"""Load all Georgia font variants using Windows AddFontResourceEx with private flag"""
custom_font_available = False
custom_font_family = None
# List of Georgia font files to load
georgia_fonts = [
"georgia.ttf", # Regular
"georgiab.ttf", # Bold
"georgiai.ttf", # Italic
"georgiaz.ttf" # Bold Italic
]
fonts_loaded = 0
try:
for font_file in georgia_fonts:
font_path = get_resource_path(os.path.join("art", font_file))
if os.path.exists(font_path):
abs_font_path = os.path.abspath(font_path)
# Use the Stack Overflow method with AddFontResourceEx
success = _load_font_private(abs_font_path)
if success:
fonts_loaded += 1
print(f"Successfully loaded font: {font_file}")
else:
print(f"Failed to load font: {font_file}")
else:
print(f"Font file not found: {font_path}")
if fonts_loaded > 0:
custom_font_available = True
custom_font_family = "Georgia"
print(f"Successfully loaded {fonts_loaded} Georgia font variants")
else:
print("No Georgia fonts could be loaded, using system fallback")
except Exception as e:
print(f"Error loading fonts: {e}")
custom_font_available = False
custom_font_family = None
return custom_font_available, custom_font_family
def _load_font_private(fontpath):
"""
Load font privately using AddFontResourceEx
Based on Stack Overflow solution by Felipe
"""
try:
from ctypes import windll, byref, create_unicode_buffer
# Constants for AddFontResourceEx
FR_PRIVATE = 0x10 # Font is private to this process
FR_NOT_ENUM = 0x20 # Font won't appear in font enumeration
# Create unicode buffer for the font path
pathbuf = create_unicode_buffer(fontpath)
# Use AddFontResourceExW for Unicode strings
AddFontResourceEx = windll.gdi32.AddFontResourceExW
# Set flags: private (unloaded when process dies) and not enumerable
flags = FR_PRIVATE | FR_NOT_ENUM
# Add the font resource
numFontsAdded = AddFontResourceEx(byref(pathbuf), flags, 0)
return numFontsAdded > 0
except Exception as e:
print(f"Error in _load_font_private: {e}")
return False
def apply_steam_styling():
"""Apply Steam Collections page inspired styling to UI components"""
return {
'button_style': {
'relief': 'flat',
'borderwidth': 0,
'highlightthickness': 0,
'font': ('Georgia', 10, 'bold'),
'cursor': 'hand2'
},
'entry_style': {
'relief': 'flat',
'borderwidth': 2,
'highlightthickness': 0,
'font': ('Georgia', 9),
'insertbackground': COLORS['text_primary']
},
'label_style': {
'font': ('Georgia', 9),
'anchor': 'w'
},
'title_style': {
'font': ('Georgia', 14, 'bold'),
'anchor': 'center'
},
'card_style': {
'relief': 'flat',
'borderwidth': 1,
'highlightthickness': 0
}
}
def get_steam_button_colors(button_type='default'):
"""Get Steam-inspired button color schemes"""
button_colors = {
'default': {
'bg': COLORS['bg_secondary'],
'fg': COLORS['text_primary'],
'activebackground': COLORS['bg_hover'],
'activeforeground': COLORS['text_highlight']
},
'primary': {
'bg': COLORS['accent_blue'],
'fg': COLORS['bg_primary'],
'activebackground': COLORS['text_highlight'],
'activeforeground': COLORS['bg_primary']
},
'success': {
'bg': COLORS['accent_green'],
'fg': COLORS['text_primary'],
'activebackground': '#6bb33f',
'activeforeground': COLORS['text_primary']
},
'warning': {
'bg': COLORS['accent_yellow'],
'fg': COLORS['bg_primary'],
'activebackground': '#ffb733',
'activeforeground': COLORS['bg_primary']
},
'danger': {
'bg': COLORS['accent_red'],
'fg': COLORS['text_primary'],
'activebackground': '#d66f6f',
'activeforeground': COLORS['text_primary']
}
}
return button_colors.get(button_type, button_colors['default'])
+1160
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -4,3 +4,4 @@ pyglet>=1.5.0
Pillow>=8.0.0
packaging>=21.0
customtkinter>=5.0.0
rich>=13.0.0
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

+1474 -716
View File
File diff suppressed because it is too large Load Diff
+258
View File
@@ -0,0 +1,258 @@
from functools import lru_cache
from PIL import Image, ImageColor, ImageDraw
FONT_FAMILY = "Georgia"
COLORS = {
"bg_primary": "#151515",
"bg_secondary": "#1d1d1d",
"bg_tertiary": "#212121",
"bg_card": "#2b2b2b",
"bg_hover": "#36312c",
"bg_error": "#2f2926",
"text_primary": "#f5f2ec",
"text_highlight": "#f0a621",
"text_body": "#c8c8c8",
"text_secondary": "#ffffff",
"text_muted": "#979797",
"accent_green": "#3a8e35",
"accent_red": "#ff4a46",
"accent_yellow": "#f0a621",
"accent_blue": "#d7b56a",
"border_light": "#4d453b",
"border_dark": "#0f0f0f",
"stripe": "#1d1d1d",
"stripe_soft": "#232323",
}
@lru_cache(maxsize=4)
def _load_rgba_source(image_path):
"""Load and cache a source image in RGBA form for repeated resizes."""
return Image.open(image_path).convert("RGBA")
def _rgba(color, alpha=255):
red, green, blue = ImageColor.getrgb(color)
return red, green, blue, alpha
def create_striped_texture(
width,
height,
*,
base_color=None,
stripe_color=None,
stripe_width=16,
stripe_gap=18,
stripe_alpha=110,
corner_radius=0,
border_color=None,
border_width=0,
border_alpha=255,
):
"""Create a dark diagonal striped texture matching the pack UI style."""
width = max(1, int(width))
height = max(1, int(height))
base = Image.new(
"RGBA",
(width, height),
_rgba(base_color or COLORS["bg_card"]),
)
stripes = Image.new("RGBA", (width, height), (0, 0, 0, 0))
stripe_draw = ImageDraw.Draw(stripes)
span = width + height
step = max(1, stripe_width + stripe_gap)
for offset in range(-height, span + height, step):
stripe_draw.line(
(offset, 0, offset + height, height),
fill=_rgba(stripe_color or COLORS["stripe"], stripe_alpha),
width=max(1, stripe_width),
)
image = Image.alpha_composite(base, stripes)
if border_color and border_width > 0:
border_draw = ImageDraw.Draw(image)
inset = max(1, border_width // 2)
bounds = (inset, inset, width - inset - 1, height - inset - 1)
if corner_radius > 0:
border_draw.rounded_rectangle(
bounds,
radius=max(1, corner_radius - inset),
outline=_rgba(border_color, border_alpha),
width=border_width,
)
else:
border_draw.rectangle(
bounds,
outline=_rgba(border_color, border_alpha),
width=border_width,
)
if corner_radius > 0:
mask = Image.new("L", (width, height), 0)
mask_draw = ImageDraw.Draw(mask)
mask_draw.rounded_rectangle(
(0, 0, width - 1, height - 1),
radius=corner_radius,
fill=255,
)
clipped = Image.new("RGBA", (width, height), (0, 0, 0, 0))
clipped.paste(image, (0, 0), mask)
image = clipped
return image
def create_striped_panel(
width,
height,
*,
panel_color=None,
stripe_color=None,
stripe_width=18,
stripe_gap=18,
stripe_alpha=110,
halo_padding=22,
halo_alpha=135,
panel_alpha=240,
corner_radius=18,
border_color=None,
border_width=1,
border_alpha=255,
):
"""Create a mostly solid panel with stripes that extend just beyond its edges."""
width = max(1, int(width))
height = max(1, int(height))
halo_padding = max(0, int(halo_padding))
total_width = width + (halo_padding * 2)
total_height = height + (halo_padding * 2)
image = Image.new("RGBA", (total_width, total_height), (0, 0, 0, 0))
stripes = Image.new("RGBA", (total_width, total_height), (0, 0, 0, 0))
stripe_draw = ImageDraw.Draw(stripes)
span = total_width + total_height
step = max(1, stripe_width + stripe_gap)
for offset in range(-total_height, span + total_height, step):
stripe_draw.line(
(offset, 0, offset + total_height, total_height),
fill=_rgba(stripe_color or COLORS["stripe"], halo_alpha),
width=max(1, stripe_width),
)
halo_mask = Image.new("L", (total_width, total_height), 0)
halo_draw = ImageDraw.Draw(halo_mask)
halo_draw.rounded_rectangle(
(0, 0, total_width - 1, total_height - 1),
radius=max(1, corner_radius + halo_padding),
fill=255,
)
halo_image = Image.new("RGBA", (total_width, total_height), (0, 0, 0, 0))
halo_image.paste(stripes, (0, 0), halo_mask)
image = Image.alpha_composite(image, halo_image)
panel_left = halo_padding
panel_top = halo_padding
panel_right = panel_left + width - 1
panel_bottom = panel_top + height - 1
panel = Image.new("RGBA", (total_width, total_height), (0, 0, 0, 0))
panel_draw = ImageDraw.Draw(panel)
panel_draw.rounded_rectangle(
(panel_left, panel_top, panel_right, panel_bottom),
radius=corner_radius,
fill=_rgba(panel_color or COLORS["bg_card"], panel_alpha),
)
if border_color and border_width > 0:
inset = max(1, border_width // 2)
panel_draw.rounded_rectangle(
(
panel_left + inset,
panel_top + inset,
panel_right - inset,
panel_bottom - inset,
),
radius=max(1, corner_radius - inset),
outline=_rgba(border_color, border_alpha),
width=border_width,
)
image = Image.alpha_composite(image, panel)
return image
def load_cover_background(image_path, width, height, *, overlay_color=None, overlay_alpha=125):
"""Load an image, scale it to cover, and apply a dark overlay for readability."""
width = max(1, int(width))
height = max(1, int(height))
image = _load_rgba_source(image_path)
source_ratio = image.width / image.height
target_ratio = width / height
if source_ratio > target_ratio:
new_height = height
new_width = int(height * source_ratio)
else:
new_width = width
new_height = int(width / source_ratio)
image = image.resize((new_width, new_height), Image.Resampling.BICUBIC)
left = (new_width - width) // 2
top = (new_height - height) // 2
image = image.crop((left, top, left + width, top + height))
overlay = Image.new(
"RGBA",
(width, height),
_rgba(overlay_color or COLORS["bg_primary"], overlay_alpha),
)
return Image.alpha_composite(image, overlay)
def style_text_button(
button,
foreground,
background,
*,
hover_foreground=None,
disabled_foreground=None,
):
"""Style a tkinter Button to look like a color-coded text action."""
button.configure(
bg=background,
fg=foreground,
activebackground=background,
activeforeground=hover_foreground or COLORS["text_primary"],
relief="flat",
bd=0,
borderwidth=0,
highlightthickness=0,
disabledforeground=disabled_foreground or COLORS["text_muted"],
cursor="hand2",
)
def center_window(window, width, height, parent=None):
"""Center a window either on its parent or the current screen."""
width = int(width)
height = int(height)
window.update_idletasks()
if parent is not None and parent.winfo_exists():
x = parent.winfo_rootx() + (parent.winfo_width() // 2) - (width // 2)
y = parent.winfo_rooty() + (parent.winfo_height() // 2) - (height // 2)
else:
x = (window.winfo_screenwidth() // 2) - (width // 2)
y = (window.winfo_screenheight() // 2) - (height // 2)
window.geometry(f"{width}x{height}+{max(0, x)}+{max(0, y)}")
+152 -62
View File
@@ -16,6 +16,7 @@ from datetime import datetime, timedelta
import os
from pathlib import Path
from update_config import get_update_config
from ui_theme import COLORS, FONT_FAMILY, style_text_button, center_window
class UpdateChecker:
def __init__(self, current_version=None):
@@ -171,6 +172,57 @@ class UpdateChecker:
return latest_release, None
def _show_notice_dialog(self, parent_window, title, message, accent_color):
"""Show a small themed notice dialog."""
dialog = tk.Toplevel(parent_window)
dialog.title(title)
dialog.configure(bg=COLORS['bg_primary'])
dialog.resizable(False, False)
dialog.attributes('-topmost', True)
panel = tk.Frame(
dialog,
bg=COLORS['bg_card'],
highlightbackground=COLORS['border_light'],
highlightcolor=COLORS['border_light'],
highlightthickness=1,
)
panel.pack(fill='both', expand=True, padx=16, pady=16)
tk.Label(
panel,
text=title,
fg=accent_color,
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 18, 'bold italic'),
).pack(pady=(18, 10))
tk.Label(
panel,
text=message,
fg=COLORS['text_body'],
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 11),
justify='center',
wraplength=340,
).pack(padx=24, pady=(0, 18))
close_btn = tk.Button(
panel,
text="Close",
command=dialog.destroy,
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
padx=0,
pady=6,
)
style_text_button(close_btn, COLORS['accent_green'], COLORS['bg_card'])
close_btn.pack(pady=(0, 18))
center_window(dialog, 420, 220, parent_window)
dialog.protocol("WM_DELETE_WINDOW", dialog.destroy)
return dialog
def show_update_dialog(self, parent_window, release_info):
"""Show an update notification dialog"""
if not release_info:
@@ -184,7 +236,7 @@ class UpdateChecker:
# Create update dialog
dialog = tk.Toplevel(parent_window)
dialog.title("Update Available - Progression Loader")
dialog.configure(bg='#2b2b2b')
dialog.configure(bg=COLORS['bg_primary'])
dialog.resizable(False, False)
dialog.attributes('-topmost', True)
@@ -195,58 +247,74 @@ class UpdateChecker:
except:
pass
# Calculate dialog size
dialog_width = 500
dialog_height = 400
dialog_height = 430
center_window(dialog, dialog_width, dialog_height, parent_window)
# Center the dialog
x = parent_window.winfo_x() + (parent_window.winfo_width() // 2) - (dialog_width // 2)
y = parent_window.winfo_y() + (parent_window.winfo_height() // 2) - (dialog_height // 2)
dialog.geometry(f"{dialog_width}x{dialog_height}+{x}+{y}")
panel = tk.Frame(
dialog,
bg=COLORS['bg_card'],
highlightbackground=COLORS['border_light'],
highlightcolor=COLORS['border_light'],
highlightthickness=1,
)
panel.pack(fill='both', expand=True, padx=16, pady=16)
# Title
title_label = tk.Label(dialog,
text="🔄 Update Available!",
fg='#00ff00', bg='#2b2b2b',
font=('Arial', 16, 'bold'))
title_label = tk.Label(
panel,
text="Update Available",
fg=COLORS['text_highlight'],
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 18, 'bold italic'),
)
title_label.pack(pady=20)
# Version info
version_frame = tk.Frame(dialog, bg='#2b2b2b')
version_frame = tk.Frame(panel, bg=COLORS['bg_card'])
version_frame.pack(pady=10)
current_label = tk.Label(version_frame,
text=f"Current Version: {self.current_version}",
fg='#cccccc', bg='#2b2b2b',
font=('Arial', 12))
fg=COLORS['text_body'], bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12))
current_label.pack()
latest_label = tk.Label(version_frame,
text=f"Latest Version: {latest_version}",
fg='#00ff00', bg='#2b2b2b',
font=('Arial', 12, 'bold'))
fg=COLORS['accent_green'], bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold'))
latest_label.pack()
# Release notes
if release_info.get('body'):
notes_label = tk.Label(dialog,
notes_label = tk.Label(panel,
text="Release Notes:",
fg='#cccccc', bg='#2b2b2b',
font=('Arial', 12, 'bold'))
fg=COLORS['text_primary'], bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'))
notes_label.pack(pady=(20, 5))
# Create scrollable text widget for release notes
notes_frame = tk.Frame(dialog, bg='#2b2b2b')
notes_frame = tk.Frame(panel, bg=COLORS['bg_card'])
notes_frame.pack(fill='both', expand=True, padx=20, pady=(0, 20))
notes_text = tk.Text(notes_frame,
height=8,
bg='#404040', fg='#ffffff',
font=('Arial', 10),
bg=COLORS['bg_tertiary'], fg=COLORS['text_body'],
font=(FONT_FAMILY, 10),
wrap=tk.WORD,
state='disabled')
state='disabled',
relief='flat',
bd=0,
insertbackground=COLORS['text_primary'],
highlightthickness=1,
highlightbackground=COLORS['border_light'])
scrollbar = tk.Scrollbar(notes_frame)
scrollbar = tk.Scrollbar(
notes_frame,
bg=COLORS['bg_tertiary'],
troughcolor=COLORS['bg_secondary'],
activebackground=COLORS['bg_hover'],
)
scrollbar.pack(side='right', fill='y')
notes_text.pack(side='left', fill='both', expand=True)
@@ -259,7 +327,7 @@ class UpdateChecker:
notes_text.config(state='disabled')
# Buttons
button_frame = tk.Frame(dialog, bg='#2b2b2b')
button_frame = tk.Frame(panel, bg=COLORS['bg_card'])
button_frame.pack(pady=20)
def download_update():
@@ -302,9 +370,10 @@ class UpdateChecker:
download_btn = tk.Button(button_frame,
text="Download Update",
command=download_update,
bg='#00aa00', fg='white',
font=('Arial', 12, 'bold'),
padx=20, pady=5)
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
padx=0, pady=6)
style_text_button(download_btn, COLORS['accent_green'], COLORS['bg_card'])
download_btn.pack(side='left', padx=5)
# Only show remind/skip buttons if persistence is enabled
@@ -312,25 +381,28 @@ class UpdateChecker:
later_btn = tk.Button(button_frame,
text="Remind Later",
command=remind_later,
bg='#0078d4', fg='white',
font=('Arial', 12),
padx=20, pady=5)
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
padx=0, pady=6)
style_text_button(later_btn, COLORS['accent_yellow'], COLORS['bg_card'])
later_btn.pack(side='left', padx=5)
skip_btn = tk.Button(button_frame,
text="Skip Version",
command=skip_version,
bg='#666666', fg='white',
font=('Arial', 12),
padx=20, pady=5)
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
padx=0, pady=6)
style_text_button(skip_btn, COLORS['accent_red'], COLORS['bg_card'])
skip_btn.pack(side='left', padx=5)
else:
close_btn = tk.Button(button_frame,
text="Close",
command=dialog.destroy,
bg='#666666', fg='white',
font=('Arial', 12),
padx=20, pady=5)
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
padx=0, pady=6)
style_text_button(close_btn, COLORS['accent_red'], COLORS['bg_card'])
close_btn.pack(side='left', padx=5)
# Handle window close
@@ -355,15 +427,21 @@ class UpdateChecker:
"""Manually check for updates and show result"""
def check_complete(release_info, error):
if error:
messagebox.showerror("Update Check Failed",
f"Could not check for updates:\n{error}",
parent=parent_window)
self._show_notice_dialog(
parent_window,
"Update Check Failed",
f"Could not check for updates:\n{error}",
COLORS['accent_red'],
)
return
if not release_info:
messagebox.showinfo("No Updates",
"Could not retrieve release information.",
parent=parent_window)
self._show_notice_dialog(
parent_window,
"No Updates",
"Could not retrieve release information.",
COLORS['accent_yellow'],
)
return
latest_version = release_info['version']
@@ -371,33 +449,44 @@ class UpdateChecker:
if self.is_newer_version(latest_version) and not self.should_skip_version(latest_version):
self.show_update_dialog(parent_window, release_info)
else:
messagebox.showinfo("Up to Date",
f"You are running the latest version ({self.current_version}).",
parent=parent_window)
self._show_notice_dialog(
parent_window,
"Up To Date",
f"You are running the latest version ({self.current_version}).",
COLORS['accent_green'],
)
# Show checking message
checking_dialog = tk.Toplevel(parent_window)
checking_dialog.title("Checking for Updates")
checking_dialog.configure(bg='#2b2b2b')
checking_dialog.configure(bg=COLORS['bg_primary'])
checking_dialog.resizable(False, False)
checking_dialog.attributes('-topmost', True)
# Center the dialog
checking_dialog.geometry("300x100")
x = parent_window.winfo_x() + (parent_window.winfo_width() // 2) - 150
y = parent_window.winfo_y() + (parent_window.winfo_height() // 2) - 50
checking_dialog.geometry(f"300x100+{x}+{y}")
panel = tk.Frame(
checking_dialog,
bg=COLORS['bg_card'],
highlightbackground=COLORS['border_light'],
highlightcolor=COLORS['border_light'],
highlightthickness=1,
)
panel.pack(fill='both', expand=True, padx=14, pady=14)
label = tk.Label(checking_dialog,
text="Checking for updates...",
fg='white', bg='#2b2b2b',
font=('Arial', 12))
label = tk.Label(
panel,
text="Checking for updates...",
fg=COLORS['text_primary'],
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 12, 'bold italic'),
)
label.pack(expand=True)
center_window(checking_dialog, 320, 120, parent_window)
def check_and_close():
release_info, error = self.check_for_updates_sync()
checking_dialog.destroy()
check_complete(release_info, error)
parent_window.after(0, lambda: checking_dialog.destroy())
parent_window.after(0, lambda: check_complete(release_info, error))
# Start check in background
threading.Thread(target=check_and_close, daemon=True).start()
@@ -452,9 +541,10 @@ def integrate_update_checker_with_gui(gui_class):
update_btn = tk.Button(parent_frame,
text="Check Updates",
command=self.manual_update_check,
bg='#404040', fg='white',
font=('Arial', 10),
padx=10, pady=2)
bg=COLORS['bg_card'],
font=(FONT_FAMILY, 10, 'bold italic'),
padx=8, pady=3)
style_text_button(update_btn, COLORS['accent_yellow'], COLORS['bg_card'])
update_btn.pack(side='right', padx=5, pady=5)
except Exception as e:
print(f"Could not add update button: {e}")
+1 -1
View File
@@ -5,7 +5,7 @@ Configuration settings for the update checker
# Update checker configuration
UPDATE_CONFIG = {
# Current version of the application
"current_version": "0.1.1",
"current_version": "0.2.1",
# Repository information
"repo_owner": "HRiggs",