pypresplash 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pypresplash-1.0.0/.gitignore +218 -0
- pypresplash-1.0.0/CMakeLists.txt +22 -0
- pypresplash-1.0.0/PKG-INFO +144 -0
- pypresplash-1.0.0/README.md +132 -0
- pypresplash-1.0.0/examples/main.py +42 -0
- pypresplash-1.0.0/pyproject.toml +23 -0
- pypresplash-1.0.0/src/bridge.cpp +32 -0
- pypresplash-1.0.0/src/include/splash_screen.h +48 -0
- pypresplash-1.0.0/src/splash_screen.cpp +337 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
project(PyPreSplash VERSION 1.0.0 LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|
6
|
+
|
|
7
|
+
find_package(Python3 COMPONENTS Interpreter Development.Module REQUIRED)
|
|
8
|
+
set(PYBIND11_FINDPYTHON ON)
|
|
9
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
10
|
+
|
|
11
|
+
pybind11_add_module(pypresplash
|
|
12
|
+
src/splash_screen.cpp
|
|
13
|
+
src/bridge.cpp
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
target_include_directories(pypresplash PRIVATE src/include)
|
|
17
|
+
target_compile_definitions(pypresplash PRIVATE UNICODE _UNICODE)
|
|
18
|
+
target_compile_definitions(pypresplash PRIVATE NOMINMAX)
|
|
19
|
+
|
|
20
|
+
target_link_libraries(pypresplash PRIVATE gdiplus user32 gdi32)
|
|
21
|
+
|
|
22
|
+
install(TARGETS pypresplash DESTINATION .)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: pypresplash
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A native Windows GDI+ splash screen module with smooth animations and transparency.
|
|
5
|
+
Author-Email: Kartavya Shukla <novfensec@protonmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# PyPreSplash
|
|
14
|
+
|
|
15
|
+
A lightning-fast, native Windows splash screen module for Python applications.
|
|
16
|
+
|
|
17
|
+
### Why does this exist?
|
|
18
|
+
Python applications (especially those using heavy libraries or frameworks like PyQt/Kivy) take a few seconds to start up. Without a splash screen, users might click your app icon, see nothing happen for 4 seconds, assume it's broken, and click it three more times.
|
|
19
|
+
|
|
20
|
+
**PyPreSplash** pops up *instantly* using the native Windows API. It gives your users immediate visual feedback (a logo and a progress bar) while Python quietly finishes doing the heavy lifting in the background.
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
* **Zero Heavy Dependencies:** Uses native Win32/GDI+ APIs. No need to load Tkinter, PyQt, or Kivy just to show a loading screen.
|
|
24
|
+
* **True Transparency:** Fully supports PNGs with transparent backgrounds. No ugly black boxes around your logo.
|
|
25
|
+
* **Smooth 60FPS Animations:** The progress bar smoothly glides to its target using a C++ micro-loop, ensuring the window stays responsive.
|
|
26
|
+
* **Minimalist UI:** The progress bar draws directly over your image edge-to-edge for a modern look.
|
|
27
|
+
* **Dynamic Styling:** Change progress bar and text colors on the fly using standard Hex codes (e.g., `#0f62fe`).
|
|
28
|
+
* **Custom Typography:** Use any system font (e.g., `"Segoe UI"`, `"Consolas"`) with hardware-accelerated ClearType rendering.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
This project is built using C++17, Pybind11, and `scikit-build-core`.
|
|
33
|
+
|
|
34
|
+
**Prerequisites:**
|
|
35
|
+
* Windows 10 or 11
|
|
36
|
+
* Python 3.8+
|
|
37
|
+
* A C++ Compiler (Visual Studio / MSVC)
|
|
38
|
+
|
|
39
|
+
### Install for Development
|
|
40
|
+
To install the module directly into your current Python environment:
|
|
41
|
+
```bash
|
|
42
|
+
pip install .
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Build a Distributable Wheel
|
|
47
|
+
|
|
48
|
+
To compile a `.whl` file that you can distribute to end-users (so they don't need a C++ compiler installed to use your app):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install build
|
|
52
|
+
python -m build --wheel
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The compiled wheel will be located in the `dist/` folder.
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
Here is a complete example of how to mask a heavy application load.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import time
|
|
64
|
+
import pypresplash
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
splash = pypresplash.SplashScreen()
|
|
68
|
+
|
|
69
|
+
splash.set_font("Segoe UI", 12)
|
|
70
|
+
|
|
71
|
+
success = splash.show("presplash.png", 0, 600, 600, "contain")
|
|
72
|
+
|
|
73
|
+
if not success:
|
|
74
|
+
print("Failed to load splash screen.")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
stages = [
|
|
78
|
+
"Initializing core subsystems...",
|
|
79
|
+
"Loading graphics engine...",
|
|
80
|
+
"Mounting file system...",
|
|
81
|
+
"Connecting to services..."
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
for i, stage_msg in enumerate(stages):
|
|
85
|
+
progress = int((i / len(stages)) * 100)
|
|
86
|
+
|
|
87
|
+
splash.set_progress(progress, stage_msg, "#0f62fe", "#FFFFFF")
|
|
88
|
+
time.sleep(0.8)
|
|
89
|
+
|
|
90
|
+
splash.set_progress(100, "Ready!", "#42be65", "#FFFFFF")
|
|
91
|
+
time.sleep(0.5)
|
|
92
|
+
|
|
93
|
+
splash.hide()
|
|
94
|
+
|
|
95
|
+
print("Main application started.")
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
main()
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## API Reference
|
|
103
|
+
|
|
104
|
+
### `SplashScreen()`
|
|
105
|
+
|
|
106
|
+
Initializes the splash screen object and starts the GDI+ subsystem.
|
|
107
|
+
|
|
108
|
+
### `.show(image_path, timeout_ms=0, width=0, height=0, scale_mode="stretch") -> bool`
|
|
109
|
+
|
|
110
|
+
Creates and displays the splash screen window.
|
|
111
|
+
|
|
112
|
+
* **`image_path`**: Path to your `.png`, `.jpg`, or `.bmp` file.
|
|
113
|
+
* **`timeout_ms`**: Optional auto-close timer in milliseconds (0 keeps it open indefinitely).
|
|
114
|
+
* **`width` / `height**`: Target window dimensions. If `0`, it defaults to the exact size of the image file.
|
|
115
|
+
* **`scale_mode`**:
|
|
116
|
+
* `"stretch"` (Default): Forces the image to exactly match the target width/height.
|
|
117
|
+
* `"contain"`: Scales the image to fit entirely inside the width/height while maintaining aspect ratio. Unused space is transparent.
|
|
118
|
+
* `"cover"`: Scales the image to fill the entire width/height, cropping edges if necessary.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
### `.set_progress(value, message="", hex_color="#00A8FF", text_color="#FFFFFF")`
|
|
123
|
+
|
|
124
|
+
Smoothly animates the progress bar to a new value.
|
|
125
|
+
|
|
126
|
+
* **`value`**: Integer between 0 and 100.
|
|
127
|
+
* **`message`**: Status text displayed just above the loading bar.
|
|
128
|
+
* **`hex_color`**: Hex string for the loading bar (e.g., `"#FF5733"`).
|
|
129
|
+
* **`text_color`**: Hex string for the status text.
|
|
130
|
+
|
|
131
|
+
### `.set_font(family, size=14)`
|
|
132
|
+
|
|
133
|
+
Configures the text rendering engine. Must be called *before* `.set_progress`.
|
|
134
|
+
|
|
135
|
+
* **`family`**: Name of an installed system font (e.g., `"Segoe UI"`, `"Consolas"`).
|
|
136
|
+
* **`size`**: Font size in points. (Recommended: 10 to 13).
|
|
137
|
+
|
|
138
|
+
### `.hide()`
|
|
139
|
+
|
|
140
|
+
Destroys the window and safely releases GDI+ resources.
|
|
141
|
+
|
|
142
|
+
### `.is_visible() -> bool`
|
|
143
|
+
|
|
144
|
+
Returns `True` if the splash screen is currently active and rendered on screen.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# PyPreSplash
|
|
2
|
+
|
|
3
|
+
A lightning-fast, native Windows splash screen module for Python applications.
|
|
4
|
+
|
|
5
|
+
### Why does this exist?
|
|
6
|
+
Python applications (especially those using heavy libraries or frameworks like PyQt/Kivy) take a few seconds to start up. Without a splash screen, users might click your app icon, see nothing happen for 4 seconds, assume it's broken, and click it three more times.
|
|
7
|
+
|
|
8
|
+
**PyPreSplash** pops up *instantly* using the native Windows API. It gives your users immediate visual feedback (a logo and a progress bar) while Python quietly finishes doing the heavy lifting in the background.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
* **Zero Heavy Dependencies:** Uses native Win32/GDI+ APIs. No need to load Tkinter, PyQt, or Kivy just to show a loading screen.
|
|
12
|
+
* **True Transparency:** Fully supports PNGs with transparent backgrounds. No ugly black boxes around your logo.
|
|
13
|
+
* **Smooth 60FPS Animations:** The progress bar smoothly glides to its target using a C++ micro-loop, ensuring the window stays responsive.
|
|
14
|
+
* **Minimalist UI:** The progress bar draws directly over your image edge-to-edge for a modern look.
|
|
15
|
+
* **Dynamic Styling:** Change progress bar and text colors on the fly using standard Hex codes (e.g., `#0f62fe`).
|
|
16
|
+
* **Custom Typography:** Use any system font (e.g., `"Segoe UI"`, `"Consolas"`) with hardware-accelerated ClearType rendering.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
This project is built using C++17, Pybind11, and `scikit-build-core`.
|
|
21
|
+
|
|
22
|
+
**Prerequisites:**
|
|
23
|
+
* Windows 10 or 11
|
|
24
|
+
* Python 3.8+
|
|
25
|
+
* A C++ Compiler (Visual Studio / MSVC)
|
|
26
|
+
|
|
27
|
+
### Install for Development
|
|
28
|
+
To install the module directly into your current Python environment:
|
|
29
|
+
```bash
|
|
30
|
+
pip install .
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Build a Distributable Wheel
|
|
35
|
+
|
|
36
|
+
To compile a `.whl` file that you can distribute to end-users (so they don't need a C++ compiler installed to use your app):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install build
|
|
40
|
+
python -m build --wheel
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The compiled wheel will be located in the `dist/` folder.
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
Here is a complete example of how to mask a heavy application load.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import time
|
|
52
|
+
import pypresplash
|
|
53
|
+
|
|
54
|
+
def main():
|
|
55
|
+
splash = pypresplash.SplashScreen()
|
|
56
|
+
|
|
57
|
+
splash.set_font("Segoe UI", 12)
|
|
58
|
+
|
|
59
|
+
success = splash.show("presplash.png", 0, 600, 600, "contain")
|
|
60
|
+
|
|
61
|
+
if not success:
|
|
62
|
+
print("Failed to load splash screen.")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
stages = [
|
|
66
|
+
"Initializing core subsystems...",
|
|
67
|
+
"Loading graphics engine...",
|
|
68
|
+
"Mounting file system...",
|
|
69
|
+
"Connecting to services..."
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
for i, stage_msg in enumerate(stages):
|
|
73
|
+
progress = int((i / len(stages)) * 100)
|
|
74
|
+
|
|
75
|
+
splash.set_progress(progress, stage_msg, "#0f62fe", "#FFFFFF")
|
|
76
|
+
time.sleep(0.8)
|
|
77
|
+
|
|
78
|
+
splash.set_progress(100, "Ready!", "#42be65", "#FFFFFF")
|
|
79
|
+
time.sleep(0.5)
|
|
80
|
+
|
|
81
|
+
splash.hide()
|
|
82
|
+
|
|
83
|
+
print("Main application started.")
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
main()
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## API Reference
|
|
91
|
+
|
|
92
|
+
### `SplashScreen()`
|
|
93
|
+
|
|
94
|
+
Initializes the splash screen object and starts the GDI+ subsystem.
|
|
95
|
+
|
|
96
|
+
### `.show(image_path, timeout_ms=0, width=0, height=0, scale_mode="stretch") -> bool`
|
|
97
|
+
|
|
98
|
+
Creates and displays the splash screen window.
|
|
99
|
+
|
|
100
|
+
* **`image_path`**: Path to your `.png`, `.jpg`, or `.bmp` file.
|
|
101
|
+
* **`timeout_ms`**: Optional auto-close timer in milliseconds (0 keeps it open indefinitely).
|
|
102
|
+
* **`width` / `height**`: Target window dimensions. If `0`, it defaults to the exact size of the image file.
|
|
103
|
+
* **`scale_mode`**:
|
|
104
|
+
* `"stretch"` (Default): Forces the image to exactly match the target width/height.
|
|
105
|
+
* `"contain"`: Scales the image to fit entirely inside the width/height while maintaining aspect ratio. Unused space is transparent.
|
|
106
|
+
* `"cover"`: Scales the image to fill the entire width/height, cropping edges if necessary.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
### `.set_progress(value, message="", hex_color="#00A8FF", text_color="#FFFFFF")`
|
|
111
|
+
|
|
112
|
+
Smoothly animates the progress bar to a new value.
|
|
113
|
+
|
|
114
|
+
* **`value`**: Integer between 0 and 100.
|
|
115
|
+
* **`message`**: Status text displayed just above the loading bar.
|
|
116
|
+
* **`hex_color`**: Hex string for the loading bar (e.g., `"#FF5733"`).
|
|
117
|
+
* **`text_color`**: Hex string for the status text.
|
|
118
|
+
|
|
119
|
+
### `.set_font(family, size=14)`
|
|
120
|
+
|
|
121
|
+
Configures the text rendering engine. Must be called *before* `.set_progress`.
|
|
122
|
+
|
|
123
|
+
* **`family`**: Name of an installed system font (e.g., `"Segoe UI"`, `"Consolas"`).
|
|
124
|
+
* **`size`**: Font size in points. (Recommended: 10 to 13).
|
|
125
|
+
|
|
126
|
+
### `.hide()`
|
|
127
|
+
|
|
128
|
+
Destroys the window and safely releases GDI+ resources.
|
|
129
|
+
|
|
130
|
+
### `.is_visible() -> bool`
|
|
131
|
+
|
|
132
|
+
Returns `True` if the splash screen is currently active and rendered on screen.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import pypresplash
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def main():
|
|
6
|
+
splash = pypresplash.SplashScreen()
|
|
7
|
+
splash.set_font("Verdana", 12)
|
|
8
|
+
|
|
9
|
+
# use any image format that works
|
|
10
|
+
success = splash.show(
|
|
11
|
+
"presplash.png", 0, 600, 300, "cover"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if not success:
|
|
15
|
+
print("Failed to initialize splash screen.")
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
stages = [
|
|
19
|
+
"Initializing core subsystems...",
|
|
20
|
+
"Loading graphics engine...",
|
|
21
|
+
"Mounting file system...",
|
|
22
|
+
"Connecting to services...",
|
|
23
|
+
"Finalizing...",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
for i, stage_msg in enumerate(stages):
|
|
27
|
+
progress = int((i / len(stages)) * 100)
|
|
28
|
+
|
|
29
|
+
splash.set_progress(progress, stage_msg, "#0f62fe", "#000000")
|
|
30
|
+
|
|
31
|
+
time.sleep(0.8)
|
|
32
|
+
|
|
33
|
+
splash.set_progress(100, "Ready!", "#42be65", "#000000")
|
|
34
|
+
time.sleep(0.5)
|
|
35
|
+
|
|
36
|
+
splash.hide()
|
|
37
|
+
|
|
38
|
+
print("Main application started.")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["scikit-build-core>=0.4.3", "pybind11"]
|
|
3
|
+
build-backend = "scikit_build_core.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pypresplash"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A native Windows GDI+ splash screen module with smooth animations and transparency."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Kartavya Shukla", email = "novfensec@protonmail.com" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: POSIX :: Linux",
|
|
19
|
+
]
|
|
20
|
+
dependencies = []
|
|
21
|
+
|
|
22
|
+
[tool.scikit-build]
|
|
23
|
+
cmake.build-type = "Release"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#include <pybind11/pybind11.h>
|
|
2
|
+
#include "splash_screen.h"
|
|
3
|
+
|
|
4
|
+
namespace py = pybind11;
|
|
5
|
+
|
|
6
|
+
PYBIND11_MODULE(pypresplash, m) {
|
|
7
|
+
m.doc() = "Native Windows GDI+ PreSplash Module";
|
|
8
|
+
|
|
9
|
+
py::class_<SplashScreen>(m, "SplashScreen")
|
|
10
|
+
.def(py::init<>())
|
|
11
|
+
.def("show", &SplashScreen::Show,
|
|
12
|
+
py::arg("image_path"),
|
|
13
|
+
py::arg("timeout_ms") = 0,
|
|
14
|
+
py::arg("width") = 0,
|
|
15
|
+
py::arg("height") = 0,
|
|
16
|
+
py::arg("scale_mode") = "stretch",
|
|
17
|
+
"Show the splash screen. scale_mode can be 'stretch', 'contain', or 'cover'.")
|
|
18
|
+
.def("hide", &SplashScreen::Hide,
|
|
19
|
+
"Hide and destroy the splash screen window.")
|
|
20
|
+
.def("set_progress", &SplashScreen::SetProgress,
|
|
21
|
+
py::arg("value"),
|
|
22
|
+
py::arg("message") = L"",
|
|
23
|
+
py::arg("hex_color") = "#00A8FF",
|
|
24
|
+
py::arg("text_color") = "#FFFFFF", // NEW Argument
|
|
25
|
+
"Update the progress bar with a target value (0-100), message, bar hex color, and text hex color.")
|
|
26
|
+
.def("set_font", &SplashScreen::SetFont,
|
|
27
|
+
py::arg("family"),
|
|
28
|
+
py::arg("size") = 14,
|
|
29
|
+
"Set the font family and size for the progress text.")
|
|
30
|
+
.def("is_visible", &SplashScreen::IsVisible,
|
|
31
|
+
"Check if the splash screen is currently active.");
|
|
32
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <windows.h>
|
|
4
|
+
#include <string>
|
|
5
|
+
|
|
6
|
+
class SplashScreen
|
|
7
|
+
{
|
|
8
|
+
public:
|
|
9
|
+
SplashScreen();
|
|
10
|
+
~SplashScreen();
|
|
11
|
+
|
|
12
|
+
bool Show(const std::wstring &imagePath, int timeoutMs = 0, int width = 0, int height = 0, const std::string &scaleMode = "stretch");
|
|
13
|
+
void Hide();
|
|
14
|
+
|
|
15
|
+
void SetProgress(int value, const std::wstring &message = L"", const std::string &hexColor = "#00A8FF", const std::string &textColor = "#FFFFFF");
|
|
16
|
+
void SetFont(const std::wstring &fontFamily, int fontSize);
|
|
17
|
+
|
|
18
|
+
bool IsVisible() const { return m_hWnd != nullptr; }
|
|
19
|
+
|
|
20
|
+
private:
|
|
21
|
+
bool CreateWindowExW();
|
|
22
|
+
void CenterWindow();
|
|
23
|
+
bool LoadSplashImage(const std::wstring &imagePath, int targetWidth, int targetHeight, const std::string &scaleMode);
|
|
24
|
+
void Render();
|
|
25
|
+
|
|
26
|
+
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
27
|
+
|
|
28
|
+
HWND m_hWnd = nullptr;
|
|
29
|
+
HINSTANCE m_hInstance = nullptr;
|
|
30
|
+
HBITMAP m_hBitmap = nullptr;
|
|
31
|
+
ULONG_PTR m_gdiplusToken = 0;
|
|
32
|
+
|
|
33
|
+
int m_width = 0;
|
|
34
|
+
int m_height = 0;
|
|
35
|
+
|
|
36
|
+
float m_currentProgress = -1.0f;
|
|
37
|
+
int m_targetProgress = -1;
|
|
38
|
+
int m_progR = 0;
|
|
39
|
+
int m_progG = 168;
|
|
40
|
+
int m_progB = 255;
|
|
41
|
+
|
|
42
|
+
int m_textR = 255;
|
|
43
|
+
int m_textG = 255;
|
|
44
|
+
int m_textB = 255;
|
|
45
|
+
std::wstring m_progressMessage;
|
|
46
|
+
std::wstring m_fontFamily = L"Segoe UI";
|
|
47
|
+
int m_fontSize = 14;
|
|
48
|
+
};
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
#include "splash_screen.h"
|
|
2
|
+
#include <gdiplus.h>
|
|
3
|
+
#include <sstream>
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
#include <stdexcept>
|
|
6
|
+
|
|
7
|
+
#pragma comment(lib, "gdiplus.lib")
|
|
8
|
+
#pragma comment(lib, "user32.lib")
|
|
9
|
+
#pragma comment(lib, "gdi32.lib")
|
|
10
|
+
|
|
11
|
+
using namespace Gdiplus;
|
|
12
|
+
|
|
13
|
+
static SplashScreen *g_pSplash = nullptr;
|
|
14
|
+
|
|
15
|
+
SplashScreen::SplashScreen()
|
|
16
|
+
{
|
|
17
|
+
m_hInstance = GetModuleHandle(nullptr);
|
|
18
|
+
GdiplusStartupInput gdiplusStartupInput;
|
|
19
|
+
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, nullptr);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
SplashScreen::~SplashScreen()
|
|
23
|
+
{
|
|
24
|
+
Hide();
|
|
25
|
+
if (m_hBitmap)
|
|
26
|
+
{
|
|
27
|
+
DeleteObject(m_hBitmap);
|
|
28
|
+
m_hBitmap = nullptr;
|
|
29
|
+
}
|
|
30
|
+
GdiplusShutdown(m_gdiplusToken);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool SplashScreen::CreateWindowExW()
|
|
34
|
+
{
|
|
35
|
+
WNDCLASS wc = {};
|
|
36
|
+
wc.lpfnWndProc = WndProc;
|
|
37
|
+
wc.hInstance = m_hInstance;
|
|
38
|
+
wc.lpszClassName = L"SplashScreenClass";
|
|
39
|
+
wc.hCursor = LoadCursor(nullptr, IDC_WAIT);
|
|
40
|
+
|
|
41
|
+
RegisterClass(&wc);
|
|
42
|
+
|
|
43
|
+
DWORD dwExStyle = WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW;
|
|
44
|
+
DWORD dwStyle = WS_POPUP;
|
|
45
|
+
|
|
46
|
+
m_hWnd = ::CreateWindowEx(
|
|
47
|
+
dwExStyle,
|
|
48
|
+
L"SplashScreenClass",
|
|
49
|
+
L"Splash",
|
|
50
|
+
dwStyle,
|
|
51
|
+
0, 0,
|
|
52
|
+
m_width, m_height,
|
|
53
|
+
nullptr, nullptr, m_hInstance, nullptr);
|
|
54
|
+
|
|
55
|
+
if (!m_hWnd)
|
|
56
|
+
return false;
|
|
57
|
+
|
|
58
|
+
// Optional: Keep rounded corners, or set to 0,0 for sharp corners
|
|
59
|
+
HRGN hRgn = CreateRoundRectRgn(0, 0, m_width, m_height, 0, 0);
|
|
60
|
+
SetWindowRgn(m_hWnd, hRgn, TRUE);
|
|
61
|
+
|
|
62
|
+
CenterWindow();
|
|
63
|
+
g_pSplash = this;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void SplashScreen::CenterWindow()
|
|
68
|
+
{
|
|
69
|
+
RECT rcWork;
|
|
70
|
+
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
|
|
71
|
+
|
|
72
|
+
int screenWidth = rcWork.right - rcWork.left;
|
|
73
|
+
int screenHeight = rcWork.bottom - rcWork.top;
|
|
74
|
+
|
|
75
|
+
int x = (screenWidth - m_width) / 2 + rcWork.left;
|
|
76
|
+
int y = (screenHeight - m_height) / 2 + rcWork.top;
|
|
77
|
+
|
|
78
|
+
SetWindowPos(m_hWnd, HWND_TOPMOST, x, y, m_width, m_height, SWP_NOZORDER);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
bool SplashScreen::LoadSplashImage(const std::wstring &imagePath, int targetWidth, int targetHeight, const std::string &scaleMode)
|
|
82
|
+
{
|
|
83
|
+
if (m_hBitmap)
|
|
84
|
+
{
|
|
85
|
+
DeleteObject(m_hBitmap);
|
|
86
|
+
m_hBitmap = nullptr;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Gdiplus::Bitmap *pBitmap = new Gdiplus::Bitmap(imagePath.c_str());
|
|
90
|
+
if (pBitmap->GetLastStatus() != Gdiplus::Ok)
|
|
91
|
+
{
|
|
92
|
+
delete pBitmap;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (targetWidth > 0 && targetHeight > 0)
|
|
97
|
+
{
|
|
98
|
+
m_width = targetWidth;
|
|
99
|
+
m_height = targetHeight;
|
|
100
|
+
|
|
101
|
+
Gdiplus::Bitmap *pScaled = new Gdiplus::Bitmap(m_width, m_height, PixelFormat32bppARGB);
|
|
102
|
+
Gdiplus::Graphics graphics(pScaled);
|
|
103
|
+
graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
|
|
104
|
+
|
|
105
|
+
float srcWidth = (float)pBitmap->GetWidth();
|
|
106
|
+
float srcHeight = (float)pBitmap->GetHeight();
|
|
107
|
+
|
|
108
|
+
float scaleX = (float)targetWidth / srcWidth;
|
|
109
|
+
float scaleY = (float)targetHeight / srcHeight;
|
|
110
|
+
|
|
111
|
+
float drawWidth = (float)targetWidth;
|
|
112
|
+
float drawHeight = (float)targetHeight;
|
|
113
|
+
float drawX = 0.0f;
|
|
114
|
+
float drawY = 0.0f;
|
|
115
|
+
|
|
116
|
+
if (scaleMode == "contain")
|
|
117
|
+
{
|
|
118
|
+
float scale = std::min(scaleX, scaleY);
|
|
119
|
+
drawWidth = srcWidth * scale;
|
|
120
|
+
drawHeight = srcHeight * scale;
|
|
121
|
+
drawX = (targetWidth - drawWidth) / 2.0f;
|
|
122
|
+
drawY = (targetHeight - drawHeight) / 2.0f;
|
|
123
|
+
}
|
|
124
|
+
else if (scaleMode == "cover")
|
|
125
|
+
{
|
|
126
|
+
float scale = std::max(scaleX, scaleY);
|
|
127
|
+
drawWidth = srcWidth * scale;
|
|
128
|
+
drawHeight = srcHeight * scale;
|
|
129
|
+
drawX = (targetWidth - drawWidth) / 2.0f;
|
|
130
|
+
drawY = (targetHeight - drawHeight) / 2.0f;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
graphics.DrawImage(pBitmap, drawX, drawY, drawWidth, drawHeight);
|
|
134
|
+
|
|
135
|
+
pScaled->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &m_hBitmap);
|
|
136
|
+
delete pScaled;
|
|
137
|
+
}
|
|
138
|
+
else
|
|
139
|
+
{
|
|
140
|
+
m_width = pBitmap->GetWidth();
|
|
141
|
+
m_height = pBitmap->GetHeight();
|
|
142
|
+
pBitmap->GetHBITMAP(Gdiplus::Color(0, 0, 0, 0), &m_hBitmap);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
delete pBitmap;
|
|
146
|
+
return m_hBitmap != nullptr;
|
|
147
|
+
}
|
|
148
|
+
void SplashScreen::Render()
|
|
149
|
+
{
|
|
150
|
+
if (!m_hWnd || !m_hBitmap)
|
|
151
|
+
return;
|
|
152
|
+
|
|
153
|
+
SIZE size = {m_width, m_height};
|
|
154
|
+
RECT rcWindow;
|
|
155
|
+
GetWindowRect(m_hWnd, &rcWindow);
|
|
156
|
+
POINT ptOrigin = {rcWindow.left, rcWindow.top};
|
|
157
|
+
|
|
158
|
+
HDC hdcScreen = GetDC(nullptr);
|
|
159
|
+
HDC hdcMem = CreateCompatibleDC(hdcScreen);
|
|
160
|
+
|
|
161
|
+
BITMAPINFO bmi = {0};
|
|
162
|
+
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
163
|
+
bmi.bmiHeader.biWidth = m_width;
|
|
164
|
+
bmi.bmiHeader.biHeight = -m_height;
|
|
165
|
+
bmi.bmiHeader.biPlanes = 1;
|
|
166
|
+
bmi.bmiHeader.biBitCount = 32;
|
|
167
|
+
bmi.bmiHeader.biCompression = BI_RGB;
|
|
168
|
+
|
|
169
|
+
void *pBits = nullptr;
|
|
170
|
+
HBITMAP hbmpMem = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
|
|
171
|
+
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, hbmpMem);
|
|
172
|
+
|
|
173
|
+
// 1. Draw Background
|
|
174
|
+
HDC hdcImage = CreateCompatibleDC(hdcScreen);
|
|
175
|
+
HBITMAP hbmpOldImage = (HBITMAP)SelectObject(hdcImage, m_hBitmap);
|
|
176
|
+
BitBlt(hdcMem, 0, 0, m_width, m_height, hdcImage, 0, 0, SRCCOPY);
|
|
177
|
+
SelectObject(hdcImage, hbmpOldImage);
|
|
178
|
+
DeleteDC(hdcImage);
|
|
179
|
+
|
|
180
|
+
// 2. Draw UI using GDI+ (Fixes transparency bugs!)
|
|
181
|
+
if (m_targetProgress >= 0)
|
|
182
|
+
{
|
|
183
|
+
Gdiplus::Graphics graphics(hdcMem);
|
|
184
|
+
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
|
|
185
|
+
graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintClearTypeGridFit);
|
|
186
|
+
|
|
187
|
+
int barHeight = 6;
|
|
188
|
+
int barX = 0;
|
|
189
|
+
int barY = m_height - barHeight;
|
|
190
|
+
int barWidth = m_width;
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
// Progress Bar
|
|
194
|
+
int filledWidth = (int)((barWidth * m_currentProgress) / 100.0f);
|
|
195
|
+
if (filledWidth > 0)
|
|
196
|
+
{
|
|
197
|
+
Gdiplus::SolidBrush progBrush(Gdiplus::Color(255, m_progR, m_progG, m_progB));
|
|
198
|
+
graphics.FillRectangle(&progBrush, barX, barY, filledWidth, barHeight);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 3. Draw Text
|
|
202
|
+
if (!m_progressMessage.empty() || m_targetProgress >= 0)
|
|
203
|
+
{
|
|
204
|
+
std::wstring displayText = m_progressMessage;
|
|
205
|
+
if (m_targetProgress >= 0) {
|
|
206
|
+
displayText += L" - " + std::to_wstring((int)m_currentProgress) + L"%";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Gdiplus::FontFamily fontFamily(m_fontFamily.c_str());
|
|
210
|
+
Gdiplus::Font font(&fontFamily, (Gdiplus::REAL)m_fontSize, Gdiplus::FontStyleRegular, Gdiplus::UnitPoint);
|
|
211
|
+
Gdiplus::StringFormat format;
|
|
212
|
+
format.SetAlignment(Gdiplus::StringAlignmentNear);
|
|
213
|
+
format.SetLineAlignment(Gdiplus::StringAlignmentFar);
|
|
214
|
+
|
|
215
|
+
int padding = 15;
|
|
216
|
+
Gdiplus::RectF textRect((Gdiplus::REAL)padding, (Gdiplus::REAL)(barY - 40), (Gdiplus::REAL)(m_width - padding * 2), 35.0f);
|
|
217
|
+
|
|
218
|
+
// Just draw the pure colored text!
|
|
219
|
+
Gdiplus::SolidBrush textBrush(Gdiplus::Color(255, m_textR, m_textG, m_textB));
|
|
220
|
+
graphics.DrawString(displayText.c_str(), -1, &font, textRect, &format, &textBrush);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 4. Update Window
|
|
225
|
+
BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
|
|
226
|
+
POINT ptZero = {0, 0};
|
|
227
|
+
UpdateLayeredWindow(m_hWnd, hdcScreen, &ptOrigin, &size,
|
|
228
|
+
hdcMem, &ptZero, RGB(0, 0, 0), &blend, ULW_ALPHA);
|
|
229
|
+
|
|
230
|
+
SelectObject(hdcMem, hbmpOld);
|
|
231
|
+
DeleteObject(hbmpMem);
|
|
232
|
+
DeleteDC(hdcMem);
|
|
233
|
+
ReleaseDC(nullptr, hdcScreen);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
void SplashScreen::SetProgress(int value, const std::wstring &message, const std::string &hexColor, const std::string &textColor)
|
|
237
|
+
{
|
|
238
|
+
if (!IsVisible())
|
|
239
|
+
return;
|
|
240
|
+
|
|
241
|
+
m_targetProgress = value;
|
|
242
|
+
m_progressMessage = message;
|
|
243
|
+
|
|
244
|
+
// Helper to parse hex strings safely
|
|
245
|
+
auto parseHex = [](const std::string &hex, int &r, int &g, int &b)
|
|
246
|
+
{
|
|
247
|
+
std::string h = hex;
|
|
248
|
+
if (!h.empty() && h[0] == '#')
|
|
249
|
+
h = h.substr(1);
|
|
250
|
+
if (h.length() == 6)
|
|
251
|
+
{
|
|
252
|
+
try
|
|
253
|
+
{
|
|
254
|
+
r = std::stoi(h.substr(0, 2), nullptr, 16);
|
|
255
|
+
g = std::stoi(h.substr(2, 2), nullptr, 16);
|
|
256
|
+
b = std::stoi(h.substr(4, 2), nullptr, 16);
|
|
257
|
+
}
|
|
258
|
+
catch (...)
|
|
259
|
+
{
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
parseHex(hexColor, m_progR, m_progG, m_progB);
|
|
265
|
+
parseHex(textColor, m_textR, m_textG, m_textB);
|
|
266
|
+
|
|
267
|
+
if (m_currentProgress < 0.0f)
|
|
268
|
+
{
|
|
269
|
+
m_currentProgress = 0.0f;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
while (m_currentProgress < (float)m_targetProgress)
|
|
273
|
+
{
|
|
274
|
+
m_currentProgress += ((float)m_targetProgress - m_currentProgress) * 0.15f;
|
|
275
|
+
|
|
276
|
+
if ((float)m_targetProgress - m_currentProgress < 0.5f)
|
|
277
|
+
{
|
|
278
|
+
m_currentProgress = (float)m_targetProgress;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
Render();
|
|
282
|
+
Sleep(16);
|
|
283
|
+
|
|
284
|
+
MSG msg;
|
|
285
|
+
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
|
|
286
|
+
{
|
|
287
|
+
TranslateMessage(&msg);
|
|
288
|
+
DispatchMessage(&msg);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
LRESULT CALLBACK SplashScreen::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
294
|
+
{
|
|
295
|
+
switch (msg)
|
|
296
|
+
{
|
|
297
|
+
case WM_DESTROY:
|
|
298
|
+
PostQuitMessage(0);
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
bool SplashScreen::Show(const std::wstring &imagePath, int timeoutMs, int width, int height, const std::string &scaleMode)
|
|
305
|
+
{
|
|
306
|
+
if (!LoadSplashImage(imagePath, width, height, scaleMode))
|
|
307
|
+
return false;
|
|
308
|
+
|
|
309
|
+
if (!CreateWindowExW())
|
|
310
|
+
return false;
|
|
311
|
+
|
|
312
|
+
Render();
|
|
313
|
+
ShowWindow(m_hWnd, SW_SHOW);
|
|
314
|
+
UpdateWindow(m_hWnd);
|
|
315
|
+
|
|
316
|
+
if (timeoutMs > 0)
|
|
317
|
+
{
|
|
318
|
+
SetTimer(m_hWnd, 1, timeoutMs, nullptr);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
void SplashScreen::Hide()
|
|
325
|
+
{
|
|
326
|
+
if (m_hWnd && IsWindow(m_hWnd))
|
|
327
|
+
{
|
|
328
|
+
DestroyWindow(m_hWnd);
|
|
329
|
+
m_hWnd = nullptr;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
void SplashScreen::SetFont(const std::wstring &fontFamily, int fontSize)
|
|
334
|
+
{
|
|
335
|
+
m_fontFamily = fontFamily;
|
|
336
|
+
m_fontSize = fontSize;
|
|
337
|
+
}
|