redesign: UI Work

This commit is contained in:
2026-04-14 13:18:27 -04:00
parent a015908d44
commit 66e7ca4fc7
11 changed files with 2130 additions and 812 deletions
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.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

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'])
+138 -88
View File
@@ -36,6 +36,23 @@ from update_config import get_update_config
# Load environment variables
load_dotenv()
# Steam Collections page inspired color scheme for TUI
TUI_COLORS = {
'bg_primary': '#1b2838', # Steam dark blue background
'bg_secondary': '#2a475e', # Steam medium blue background
'bg_tertiary': '#1e2328', # Steam darker background for cards
'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
'accent_cyan': '#66c0f4', # Steam info/header color (same as blue)
}
console = Console()
class ProgressionTUI:
@@ -114,23 +131,26 @@ class ProgressionTUI:
self.hr_systems_logo = "Hudson Riggs Systems"
def display_header(self):
"""Display the header with ASCII art"""
# Use the actual progression logo ASCII art instead of just text
"""Display the header with ASCII art using Steam Collections inspired styling"""
# Use the actual progression logo ASCII art with Steam blue highlight
header_panel = Panel(
Align.center(self.progression_logo),
box=box.DOUBLE,
style="cyan",
padding=(1, 2)
style=TUI_COLORS['accent_blue'], # Steam blue
border_style=TUI_COLORS['accent_blue'],
padding=(1, 2),
title="[bold]PROGRESSION LOADER[/bold]",
title_align="center"
)
return header_panel
def display_footer(self):
"""Display footer with HR Systems info"""
"""Display footer with HR Systems info using Steam Collections inspired styling"""
footer_text = Text()
footer_text.append("Hudson Riggs Systems", style="dim white")
footer_text.append(" - ", style="dim white")
footer_text.append("https://hudsonriggs.systems", style="dim blue underline")
footer_text.append("Hudson Riggs Systems", style=f"dim {TUI_COLORS['text_secondary']}")
footer_text.append(" - ", style=f"dim {TUI_COLORS['text_secondary']}")
footer_text.append("https://hudsonriggs.systems", style=f"dim {TUI_COLORS['accent_blue']} underline")
return Align.center(footer_text)
@@ -147,40 +167,66 @@ class ProgressionTUI:
self.console.print(self.display_header())
self.console.print()
# Create main menu options
menu_table = Table(show_header=False, box=box.SIMPLE, padding=(0, 2))
menu_table.add_column("Option", style="cyan bold", width=4)
menu_table.add_column("Description", style="white")
menu_table.add_column("Status", style="green", width=15)
# Create main menu options with Steam Collections inspired styling
menu_table = Table(show_header=False, box=box.ROUNDED, padding=(0, 2))
menu_table.add_column("Option", style=f"{TUI_COLORS['accent_blue']} bold", width=4)
menu_table.add_column("Description", style=TUI_COLORS['text_primary'])
menu_table.add_column("Status", style=TUI_COLORS['accent_green'], width=15)
# Menu options with status indicators
rimworld_status = "Valid" if self.is_rimworld_valid else "Not Set"
rimworld_color = "green" if self.is_rimworld_valid else "red"
# Menu options with Steam-styled status indicators
rimworld_status = "✓ Valid" if self.is_rimworld_valid else "Not Set"
rimworld_color = TUI_COLORS['accent_green'] if self.is_rimworld_valid else TUI_COLORS['accent_yellow']
menu_table.add_row("1", "Set RimWorld Game Folder", f"[{rimworld_color}]{rimworld_status}[/{rimworld_color}]")
menu_table.add_row("2", "Check Steam Workshop Subscriptions", "")
menu_table.add_row("3", "Check RimWorld Expansions", "")
menu_table.add_row("4", "Load Progression Pack Complete", "Enabled" if self.is_rimworld_valid else "[dim]Disabled[/dim]")
menu_table.add_row("5", "Merge with Current Mods Config", "Enabled" if self.is_rimworld_valid else "[dim]Disabled[/dim]")
menu_table.add_row("4", "Load Progression Pack Complete", f"[{TUI_COLORS['accent_green']}]✓ Ready[/{TUI_COLORS['accent_green']}]" if self.is_rimworld_valid else f"[{TUI_COLORS['text_muted']}]⚠ Disabled[/{TUI_COLORS['text_muted']}]")
menu_table.add_row("5", "Merge with Current Mods Config", f"[{TUI_COLORS['accent_green']}]✓ Ready[/{TUI_COLORS['accent_green']}]" if self.is_rimworld_valid else f"[{TUI_COLORS['text_muted']}]⚠ Disabled[/{TUI_COLORS['text_muted']}]")
menu_table.add_row("6", "Check for Updates", "")
menu_table.add_row("7", "View Configuration", "")
menu_table.add_row("0", "Exit", "")
# Create Steam-styled menu panel
menu_panel = Panel(
menu_table,
title="Main Menu",
title="[bold]Main Menu[/bold]",
title_align="center",
box=box.ROUNDED,
style="white"
style=TUI_COLORS['text_primary'],
border_style=TUI_COLORS['accent_blue']
)
self.console.print(menu_panel)
self.console.print()
# Display current paths if set
# Display current paths if set with Steam Collections styling
if self.rimworld_path or self.workshop_path:
info_table = Table(show_header=False, box=None, padding=(0, 1))
info_table.add_column("Label", style="cyan", width=25)
info_table.add_column("Path", style="white")
info_table.add_column("Label", style=f"{TUI_COLORS['accent_blue']} bold", width=25)
info_table.add_column("Path", style=TUI_COLORS['text_body'])
if self.rimworld_path:
status_icon = "" if self.is_rimworld_valid else ""
status_color = TUI_COLORS['accent_green'] if self.is_rimworld_valid else TUI_COLORS['accent_yellow']
info_table.add_row(f"[{status_color}]{status_icon}[/{status_color}] RimWorld Path:", self.rimworld_path)
if self.workshop_path:
info_table.add_row(f"[{TUI_COLORS['accent_blue']}]📁[/{TUI_COLORS['accent_blue']}] Workshop Path:", self.workshop_path)
if self.modsconfig_path:
info_table.add_row(f"[{TUI_COLORS['accent_blue']}]⚙[/{TUI_COLORS['accent_blue']}] ModsConfig Path:", self.modsconfig_path)
# Create Steam-styled info panel
info_panel = Panel(
info_table,
title="[bold]Current Configuration[/bold]",
title_align="center",
box=box.ROUNDED,
style=TUI_COLORS['text_secondary'],
border_style=TUI_COLORS['text_muted']
)
self.console.print(info_panel)
self.console.print()
if self.rimworld_path:
info_table.add_row("RimWorld Path:", self.rimworld_path)
@@ -201,7 +247,7 @@ class ProgressionTUI:
choice = Prompt.ask("Select an option", choices=["0", "1", "2", "3", "4", "5", "6", "7"], default="1")
if choice == "0":
self.console.print("\n[yellow]Thank you for using Progression Loader![/yellow]")
self.console.print(f"\n[{TUI_COLORS['accent_yellow']}]Thank you for using Progression Loader![/{TUI_COLORS['accent_yellow']}]")
break
elif choice == "1":
self.set_rimworld_path()
@@ -213,13 +259,13 @@ class ProgressionTUI:
if self.is_rimworld_valid:
self.load_progression_pack()
else:
self.console.print("\n[red]Please set a valid RimWorld path first![/red]")
self.console.print(f"\n[{TUI_COLORS['accent_red']}]Please set a valid RimWorld path first![/{TUI_COLORS['accent_red']}]")
self.console.input("\nPress Enter to continue...")
elif choice == "5":
if self.is_rimworld_valid:
self.merge_with_current_mods()
else:
self.console.print("\n[red]Please set a valid RimWorld path first![/red]")
self.console.print(f"\n[{TUI_COLORS['accent_red']}]Please set a valid RimWorld path first![/{TUI_COLORS['accent_red']}]")
self.console.input("\nPress Enter to continue...")
elif choice == "6":
self.check_for_updates()
@@ -227,10 +273,10 @@ class ProgressionTUI:
self.view_configuration()
except KeyboardInterrupt:
self.console.print("\n\n[yellow]Goodbye![/yellow]")
self.console.print(f"\n\n[{TUI_COLORS['accent_yellow']}]Goodbye![/{TUI_COLORS['accent_yellow']}]")
break
except Exception as e:
self.console.print(f"\n[red]Error: {e}[/red]")
self.console.print(f"\n[{TUI_COLORS['accent_red']}]Error: {e}[/{TUI_COLORS['accent_red']}]")
self.console.input("\nPress Enter to continue...")
def set_rimworld_path(self):
@@ -239,28 +285,30 @@ class ProgressionTUI:
self.console.print(self.display_header())
self.console.print()
# Instructions
# Instructions with modern styling - bold titles, italic emphasis
instructions = Panel(
Text.from_markup(
"To find your RimWorld path:\n"
"[bold]To find your RimWorld path:[/bold]\n\n"
"1. Open Steam\n"
"2. Right-click RimWorld in your library\n"
"3. Select 'Manage' > 'Browse local files'\n"
"4. Copy the path from the address bar\n\n"
"Example: C:\\Steam\\steamapps\\common\\RimWorld"
"[italic dim]Example: C:\\Steam\\steamapps\\common\\RimWorld[/italic dim]"
),
title="Instructions",
style="cyan"
title=f"[bold {TUI_COLORS['text_highlight']}]Instructions[/bold {TUI_COLORS['text_highlight']}]",
title_align="center",
style=TUI_COLORS['accent_cyan'],
border_style="rounded"
)
self.console.print(instructions)
self.console.print()
# Current path if set
# Current path if set with new colors
if self.rimworld_path:
current_panel = Panel(
f"Current path: {self.rimworld_path}",
title="Current RimWorld Path",
style="green" if self.is_rimworld_valid else "red"
style=TUI_COLORS['accent_green'] if self.is_rimworld_valid else TUI_COLORS['accent_red']
)
self.console.print(current_panel)
self.console.print()
@@ -283,17 +331,17 @@ class ProgressionTUI:
# Derive workshop path
self.derive_workshop_path()
progress.update(task, description="[green]Valid RimWorld installation found![/green]")
progress.update(task, description=f"[{TUI_COLORS['accent_green']}]Valid RimWorld installation found![/{TUI_COLORS['accent_green']}]")
time.sleep(1)
self.console.print(f"\n[green]RimWorld path set successfully![/green]")
self.console.print(f"\n[{TUI_COLORS['accent_green']}]RimWorld path set successfully![/{TUI_COLORS['accent_green']}]")
self.console.print(f"Workshop path: {self.workshop_path}")
else:
progress.update(task, description="[red]Invalid RimWorld path![/red]")
progress.update(task, description=f"[{TUI_COLORS['accent_red']}]Invalid RimWorld path![/{TUI_COLORS['accent_red']}]")
time.sleep(1)
self.console.print(f"\n[red]Invalid RimWorld installation at: {new_path}[/red]")
self.console.print("[red]Please ensure the path contains RimWorld.exe and Data folder[/red]")
self.console.print(f"\n[{TUI_COLORS['accent_red']}]Invalid RimWorld installation at: {new_path}[/{TUI_COLORS['accent_red']}]")
self.console.print(f"[{TUI_COLORS['accent_red']}]Please ensure the path contains RimWorld.exe and Data folder[/{TUI_COLORS['accent_red']}]")
self.console.input("\nPress Enter to continue...")
@@ -339,26 +387,28 @@ class ProgressionTUI:
self.console.print(self.display_header())
self.console.print()
# Display collections
collections_table = Table(show_header=True, box=box.ROUNDED)
collections_table.add_column("Collection", style="cyan bold", width=20)
collections_table.add_column("URL", style="blue", width=50)
collections_table.add_column("Subscribed", style="green", width=12)
# Display collections with modern styling
collections_table = Table(show_header=True, box=box.ROUNDED, border_style="bright_black")
collections_table.add_column("Collection", style=f"{TUI_COLORS['text_highlight']} bold", width=20)
collections_table.add_column("URL", style=TUI_COLORS['accent_blue'], width=50)
collections_table.add_column("Subscribed", style=TUI_COLORS['accent_green'], width=12)
for name, url in self.collections.items():
status = "Yes" if self.subscription_status[name] else "No"
status_color = "green" if self.subscription_status[name] else "red"
status_color = TUI_COLORS['accent_green'] if self.subscription_status[name] else TUI_COLORS['accent_red']
collections_table.add_row(name, url, f"[{status_color}]{status}[/{status_color}]")
collections_panel = Panel(
collections_table,
title="Steam Workshop Collections",
style="white"
title="[bold]Steam Workshop Collections[/bold]",
title_align="center",
style=TUI_COLORS['text_primary'],
border_style="rounded"
)
self.console.print(collections_panel)
self.console.print()
# Instructions
# Instructions with new color scheme
instructions = Panel(
Text.from_markup(
"To subscribe to collections:\n"
@@ -367,10 +417,10 @@ class ProgressionTUI:
"3. Click 'Subscribe' on the Steam Workshop page\n"
"4. Wait for Steam to download the collection\n"
"5. Return here and mark as subscribed\n\n"
"[yellow]All collections are required for the complete Progression experience.[/yellow]"
f"[{TUI_COLORS['accent_yellow']}]All collections are required for the complete Progression experience.[/{TUI_COLORS['accent_yellow']}]"
),
title="Instructions",
style="cyan"
style=TUI_COLORS['accent_cyan']
)
self.console.print(instructions)
self.console.print()
@@ -378,8 +428,8 @@ class ProgressionTUI:
# Menu options
while True:
menu_table = Table(show_header=False, box=box.SIMPLE, padding=(0, 2))
menu_table.add_column("Option", style="cyan bold", width=4)
menu_table.add_column("Description", style="white")
menu_table.add_column("Option", style=f"{TUI_COLORS['accent_cyan']} bold", width=4)
menu_table.add_column("Description", style=TUI_COLORS['text_primary'])
menu_table.add_row("1", "Open Core Collection in browser")
menu_table.add_row("2", "Open Content Collection in browser")
@@ -390,7 +440,7 @@ class ProgressionTUI:
menu_table.add_row("7", "Check if all subscribed")
menu_table.add_row("0", "Back to main menu")
self.console.print(Panel(menu_table, title="Subscription Menu", style="white"))
self.console.print(Panel(menu_table, title="Subscription Menu", style=TUI_COLORS['text_primary']))
self.console.print()
choice = Prompt.ask("Select an option", choices=["0", "1", "2", "3", "4", "5", "6", "7"])
@@ -399,29 +449,29 @@ class ProgressionTUI:
break
elif choice == "1":
webbrowser.open(self.collections["Core Collection"])
self.console.print("[green]Opened Core Collection in browser[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Opened Core Collection in browser[/{TUI_COLORS['accent_green']}]")
elif choice == "2":
webbrowser.open(self.collections["Content Collection"])
self.console.print("[green]Opened Content Collection in browser[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Opened Content Collection in browser[/{TUI_COLORS['accent_green']}]")
elif choice == "3":
webbrowser.open(self.collections["Cosmetics Collection"])
self.console.print("[green]Opened Cosmetics Collection in browser[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Opened Cosmetics Collection in browser[/{TUI_COLORS['accent_green']}]")
elif choice == "4":
self.subscription_status["Core Collection"] = True
self.console.print("[green]Core Collection marked as subscribed[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Core Collection marked as subscribed[/{TUI_COLORS['accent_green']}]")
elif choice == "5":
self.subscription_status["Content Collection"] = True
self.console.print("[green]Content Collection marked as subscribed[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Content Collection marked as subscribed[/{TUI_COLORS['accent_green']}]")
elif choice == "6":
self.subscription_status["Cosmetics Collection"] = True
self.console.print("[green]Cosmetics Collection marked as subscribed[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]Cosmetics Collection marked as subscribed[/{TUI_COLORS['accent_green']}]")
elif choice == "7":
all_subscribed = all(self.subscription_status.values())
if all_subscribed:
self.console.print("[green]All collections are subscribed! You can proceed.[/green]")
self.console.print(f"[{TUI_COLORS['accent_green']}]All collections are subscribed! You can proceed.[/{TUI_COLORS['accent_green']}]")
else:
missing = [name for name, status in self.subscription_status.items() if not status]
self.console.print(f"[red]Missing subscriptions: {', '.join(missing)}[/red]")
self.console.print(f"[{TUI_COLORS['accent_red']}]Missing subscriptions: {', '.join(missing)}[/{TUI_COLORS['accent_red']}]")
self.console.print()
@@ -441,44 +491,44 @@ class ProgressionTUI:
# Check expansions
self.check_expansions_thread()
progress.update(task, description="[green]Expansion check complete![/green]")
progress.update(task, description=f"[{TUI_COLORS['accent_green']}]Expansion check complete![/{TUI_COLORS['accent_green']}]")
time.sleep(1)
# Display results
# Display results with new color scheme
if self.expansion_check_results:
expansion_table = Table(show_header=True, box=box.ROUNDED)
expansion_table.add_column("Expansion", style="cyan bold", width=15)
expansion_table.add_column("Status", style="white", width=10)
expansion_table.add_column("Expansion", style=f"{TUI_COLORS['accent_cyan']} bold", width=15)
expansion_table.add_column("Status", style=TUI_COLORS['text_primary'], width=10)
missing_expansions = []
for expansion, owned in self.expansion_check_results.items():
status = "Owned" if owned else "Missing"
status_color = "green" if owned else "red"
status_color = TUI_COLORS['accent_green'] if owned else TUI_COLORS['accent_red']
expansion_table.add_row(expansion, f"[{status_color}]{status}[/{status_color}]")
if not owned:
missing_expansions.append(expansion)
self.console.print(Panel(expansion_table, title="RimWorld Expansions", style="white"))
self.console.print(Panel(expansion_table, title="RimWorld Expansions", style=TUI_COLORS['text_primary']))
self.console.print()
if missing_expansions:
warning_text = Text()
warning_text.append("WARNING: ", style="bold red")
warning_text.append(f"You don't own {', '.join(missing_expansions)}!\n", style="red")
warning_text.append("Some progression mods may not work without these expansions.", style="yellow")
warning_text.append("WARNING: ", style=f"bold {TUI_COLORS['accent_red']}")
warning_text.append(f"You don't own {', '.join(missing_expansions)}!\n", style=TUI_COLORS['accent_red'])
warning_text.append("Some progression mods may not work without these expansions.", style=TUI_COLORS['accent_yellow'])
warning_panel = Panel(warning_text, title="Missing Expansions", style="red")
warning_panel = Panel(warning_text, title="Missing Expansions", style=TUI_COLORS['accent_red'])
self.console.print(warning_panel)
else:
success_panel = Panel(
"[green]All RimWorld expansions detected![/green]",
f"[{TUI_COLORS['accent_green']}]All RimWorld expansions detected![/{TUI_COLORS['accent_green']}]",
title="Expansion Check",
style="green"
style=TUI_COLORS['accent_green']
)
self.console.print(success_panel)
else:
self.console.print("[yellow]Could not check expansions. Please ensure RimWorld path is set correctly.[/yellow]")
self.console.print(f"[{TUI_COLORS['accent_yellow']}]Could not check expansions. Please ensure RimWorld path is set correctly.[/{TUI_COLORS['accent_yellow']}]")
self.console.input("\nPress Enter to continue...")
@@ -680,25 +730,25 @@ class ProgressionTUI:
self.console.print(self.display_header())
self.console.print()
# Check prerequisites
# Check prerequisites with new colors
all_subscribed = all(self.subscription_status.values())
if not all_subscribed:
missing = [name for name, status in self.subscription_status.items() if not status]
error_panel = Panel(
f"[red]Please subscribe to all collections first!\nMissing: {', '.join(missing)}[/red]",
f"[{TUI_COLORS['accent_red']}]Please subscribe to all collections first!\nMissing: {', '.join(missing)}[/{TUI_COLORS['accent_red']}]",
title="Prerequisites Not Met",
style="red"
style=TUI_COLORS['accent_red']
)
self.console.print(error_panel)
self.console.input("\nPress Enter to continue...")
return
# Confirm action
# Confirm action with new colors
confirm_text = Text()
confirm_text.append("This will replace your current ModsConfig.xml with the complete Progression pack.\n", style="yellow")
confirm_text.append("Your current mod configuration will be backed up.", style="white")
confirm_text.append("This will replace your current ModsConfig.xml with the complete Progression pack.\n", style=TUI_COLORS['accent_yellow'])
confirm_text.append("Your current mod configuration will be backed up.", style=TUI_COLORS['text_primary'])
confirm_panel = Panel(confirm_text, title="Confirmation", style="yellow")
confirm_panel = Panel(confirm_text, title="Confirmation", style=TUI_COLORS['accent_yellow'])
self.console.print(confirm_panel)
self.console.print()
@@ -1075,18 +1125,18 @@ class ProgressionTUI:
def main():
"""Main entry point for the TUI application"""
try:
# Check for updates first
# Check for updates first with new colors
config = get_update_config()
if config.get("check_on_startup", True):
console.print("[cyan]Checking for updates...[/cyan]")
console.print(f"[{TUI_COLORS['accent_cyan']}]Checking for updates...[/{TUI_COLORS['accent_cyan']}]")
try:
update_checker = UpdateChecker(config["current_version"])
release_info, error = update_checker.check_for_updates_sync()
if release_info and update_checker.is_newer_version(release_info['version']):
console.print(f"[yellow]Update available: {release_info['version']}[/yellow]")
console.print(f"[{TUI_COLORS['accent_yellow']}]Update available: {release_info['version']}[/{TUI_COLORS['accent_yellow']}]")
if config.get("updates_required", False):
console.print("[red]This update is required. Please update before continuing.[/red]")
console.print(f"[{TUI_COLORS['accent_red']}]This update is required. Please update before continuing.[/{TUI_COLORS['accent_red']}]")
if Confirm.ask("Open download page?"):
webbrowser.open(release_info['html_url'])
return
@@ -1100,9 +1150,9 @@ def main():
app.show_main_menu()
except KeyboardInterrupt:
console.print("\n[yellow]Goodbye![/yellow]")
console.print(f"\n[{TUI_COLORS['accent_yellow']}]Goodbye![/{TUI_COLORS['accent_yellow']}]")
except Exception as e:
console.print(f"[red]Fatal error: {e}[/red]")
console.print(f"[{TUI_COLORS['accent_red']}]Fatal error: {e}[/{TUI_COLORS['accent_red']}]")
console.print("[dim]Please report this error to the developers.[/dim]")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

+1179 -617
View File
File diff suppressed because it is too large Load Diff
+250
View File
@@ -0,0 +1,250 @@
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",
}
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 = Image.open(image_path).convert("RGBA")
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.LANCZOS)
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)}")
+148 -58
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",
self._show_notice_dialog(
parent_window,
"Update Check Failed",
f"Could not check for updates:\n{error}",
parent=parent_window)
COLORS['accent_red'],
)
return
if not release_info:
messagebox.showinfo("No Updates",
self._show_notice_dialog(
parent_window,
"No Updates",
"Could not retrieve release information.",
parent=parent_window)
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",
self._show_notice_dialog(
parent_window,
"Up To Date",
f"You are running the latest version ({self.current_version}).",
parent=parent_window)
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,
label = tk.Label(
panel,
text="Checking for updates...",
fg='white', bg='#2b2b2b',
font=('Arial', 12))
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}")