crystalwindow 2.5.7__py3-none-any.whl → 2.7.8__py3-none-any.whl
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.
Potentially problematic release.
This version of crystalwindow might be problematic. Click here for more details.
- crystalwindow/FileHelper.py +142 -0
- crystalwindow/Icons/default_icon.png +0 -0
- crystalwindow/__init__.py +57 -0
- crystalwindow/animation.py +15 -0
- crystalwindow/assets.py +30 -0
- crystalwindow/collision.py +15 -0
- crystalwindow/draw_helpers.py +23 -0
- crystalwindow/draw_rects.py +36 -0
- crystalwindow/draw_text_helper.py +46 -0
- crystalwindow/draw_tool.py +49 -0
- crystalwindow/fun_helpers.py +28 -0
- crystalwindow/gametests/__init__.py +14 -0
- crystalwindow/gametests/__main__.py +23 -0
- crystalwindow/gametests/gravitytest.py +47 -0
- crystalwindow/gametests/guitesting.py +56 -0
- crystalwindow/gametests/sandbox.py +49 -0
- crystalwindow/gametests/windowtesting.py +5 -0
- crystalwindow/gravity.py +75 -0
- crystalwindow/gui.py +69 -0
- crystalwindow/gui_ext.py +66 -0
- crystalwindow/math.py +40 -0
- crystalwindow/player.py +106 -0
- crystalwindow/sprites.py +37 -0
- crystalwindow/tilemap.py +13 -0
- crystalwindow/ver_warner.py +54 -0
- crystalwindow/window.py +218 -0
- {crystalwindow-2.5.7.dist-info → crystalwindow-2.7.8.dist-info}/METADATA +3 -1
- crystalwindow-2.7.8.dist-info/RECORD +31 -0
- crystalwindow-2.7.8.dist-info/licenses/LICENSE +21 -0
- crystalwindow-2.7.8.dist-info/top_level.txt +1 -0
- crystalwindow-2.5.7.dist-info/RECORD +0 -4
- crystalwindow-2.5.7.dist-info/top_level.txt +0 -1
- {crystalwindow-2.5.7.dist-info → crystalwindow-2.7.8.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CrystalWindow FileHelper
|
|
3
|
+
------------------------
|
|
4
|
+
A utility class that handles saving/loading text, JSON, and pickle files,
|
|
5
|
+
with optional Tk file dialogs and a default 'saves' directory.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
import pickle
|
|
11
|
+
import tkinter as tk
|
|
12
|
+
from tkinter import filedialog
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FileHelper:
|
|
16
|
+
"""CrystalWindow integrated file helper with default folders & Tk dialogs."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, default_save_folder="saves"):
|
|
19
|
+
"""
|
|
20
|
+
Initialize the FileHelper.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
default_save_folder (str): Folder to save files in by default.
|
|
24
|
+
Created automatically if missing.
|
|
25
|
+
"""
|
|
26
|
+
self.default_save_folder = default_save_folder
|
|
27
|
+
os.makedirs(self.default_save_folder, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
# -------------------------------------------------------------------------
|
|
30
|
+
# TK FILE DIALOGS
|
|
31
|
+
# -------------------------------------------------------------------------
|
|
32
|
+
def ask_save_file(self, default_name="save.json",
|
|
33
|
+
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]):
|
|
34
|
+
"""Open a Tkinter Save dialog starting in the default folder."""
|
|
35
|
+
root = tk.Tk()
|
|
36
|
+
root.withdraw()
|
|
37
|
+
path = filedialog.asksaveasfilename(
|
|
38
|
+
title="Save As",
|
|
39
|
+
initialdir=self.default_save_folder,
|
|
40
|
+
initialfile=default_name,
|
|
41
|
+
filetypes=filetypes,
|
|
42
|
+
defaultextension=filetypes[0][1]
|
|
43
|
+
)
|
|
44
|
+
root.destroy()
|
|
45
|
+
return path if path else None # Return None if cancelled
|
|
46
|
+
|
|
47
|
+
def ask_open_file(self,
|
|
48
|
+
filetypes=[("JSON files", "*.json"), ("All files", "*.*")]):
|
|
49
|
+
"""Open a Tkinter Open dialog starting in the default folder."""
|
|
50
|
+
root = tk.Tk()
|
|
51
|
+
root.withdraw()
|
|
52
|
+
path = filedialog.askopenfilename(
|
|
53
|
+
title="Open File",
|
|
54
|
+
initialdir=self.default_save_folder,
|
|
55
|
+
filetypes=filetypes
|
|
56
|
+
)
|
|
57
|
+
root.destroy()
|
|
58
|
+
return path if path else None
|
|
59
|
+
|
|
60
|
+
# -------------------------------------------------------------------------
|
|
61
|
+
# INTERNAL PATH HELPER
|
|
62
|
+
# -------------------------------------------------------------------------
|
|
63
|
+
def _resolve_path(self, filename):
|
|
64
|
+
"""Return a full path, resolving relative paths to the default folder."""
|
|
65
|
+
if not filename:
|
|
66
|
+
return None
|
|
67
|
+
if os.path.isabs(filename):
|
|
68
|
+
return filename
|
|
69
|
+
return os.path.join(self.default_save_folder, filename)
|
|
70
|
+
|
|
71
|
+
# -------------------------------------------------------------------------
|
|
72
|
+
# TEXT FILES
|
|
73
|
+
# -------------------------------------------------------------------------
|
|
74
|
+
def save_text(self, filename, content):
|
|
75
|
+
"""Save plain text data to a file."""
|
|
76
|
+
path = self._resolve_path(filename)
|
|
77
|
+
if not path:
|
|
78
|
+
print("[CANCELLED] No save path provided.")
|
|
79
|
+
return None
|
|
80
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
81
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
82
|
+
f.write(content)
|
|
83
|
+
print(f"[INFO] Text saved to: {path}")
|
|
84
|
+
return path
|
|
85
|
+
|
|
86
|
+
def load_text(self, filename):
|
|
87
|
+
"""Load plain text data from a file."""
|
|
88
|
+
path = self._resolve_path(filename)
|
|
89
|
+
if path and os.path.exists(path):
|
|
90
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
91
|
+
return f.read()
|
|
92
|
+
print(f"[WARN] Text file not found: {path}")
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
# -------------------------------------------------------------------------
|
|
96
|
+
# JSON FILES
|
|
97
|
+
# -------------------------------------------------------------------------
|
|
98
|
+
def save_json(self, filename, data):
|
|
99
|
+
"""Save JSON-serializable data."""
|
|
100
|
+
path = self._resolve_path(filename)
|
|
101
|
+
if not path:
|
|
102
|
+
print("[CANCELLED] No save path provided.")
|
|
103
|
+
return None
|
|
104
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
105
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
106
|
+
json.dump(data, f, indent=4)
|
|
107
|
+
print(f"[INFO] JSON saved to: {path}")
|
|
108
|
+
return path
|
|
109
|
+
|
|
110
|
+
def load_json(self, filename):
|
|
111
|
+
"""Load JSON data."""
|
|
112
|
+
path = self._resolve_path(filename)
|
|
113
|
+
if path and os.path.exists(path):
|
|
114
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
115
|
+
return json.load(f)
|
|
116
|
+
print(f"[WARN] JSON file not found: {path}")
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
# -------------------------------------------------------------------------
|
|
120
|
+
# PICKLE FILES
|
|
121
|
+
# -------------------------------------------------------------------------
|
|
122
|
+
def save_pickle(self, filename, obj):
|
|
123
|
+
"""Save a Python object using pickle."""
|
|
124
|
+
path = self._resolve_path(filename)
|
|
125
|
+
if not path:
|
|
126
|
+
print("[CANCELLED] No save path provided.")
|
|
127
|
+
return None
|
|
128
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
129
|
+
with open(path, "wb") as f:
|
|
130
|
+
pickle.dump(obj, f)
|
|
131
|
+
print(f"[INFO] Pickle saved to: {path}")
|
|
132
|
+
return path
|
|
133
|
+
|
|
134
|
+
def load_pickle(self, filename):
|
|
135
|
+
"""Load a pickled Python object."""
|
|
136
|
+
path = self._resolve_path(filename)
|
|
137
|
+
if path and os.path.exists(path):
|
|
138
|
+
with open(path, "rb") as f:
|
|
139
|
+
return pickle.load(f)
|
|
140
|
+
print(f"[WARN] Pickle file not found: {path}")
|
|
141
|
+
return None
|
|
142
|
+
|
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# crystalwindow/__init__.py
|
|
2
|
+
# 💎 CrystalWindow - Master Import Hub
|
|
3
|
+
from .ver_warner import check_for_update
|
|
4
|
+
check_for_update("crystalwindow")
|
|
5
|
+
|
|
6
|
+
# === Core Systems ===
|
|
7
|
+
from .window import Window
|
|
8
|
+
from .sprites import Sprite
|
|
9
|
+
from .tilemap import TileMap
|
|
10
|
+
from .player import Player
|
|
11
|
+
from .gravity import Gravity
|
|
12
|
+
from .FileHelper import FileHelper
|
|
13
|
+
from .math import Mathematics
|
|
14
|
+
|
|
15
|
+
# === Assets & Animation ===
|
|
16
|
+
from .assets import load_image, load_folder_images, load_music, play_music
|
|
17
|
+
from .animation import Animation
|
|
18
|
+
|
|
19
|
+
# === Collision ===
|
|
20
|
+
from .collision import check_collision, resolve_collision
|
|
21
|
+
|
|
22
|
+
# === GUI & Extensions ===
|
|
23
|
+
from .gui import Button, Label, GUIManager, random_color, hex_to_rgb
|
|
24
|
+
from .gui_ext import Toggle, Slider
|
|
25
|
+
|
|
26
|
+
# === Drawing Helpers ===
|
|
27
|
+
from .draw_helpers import gradient_rect, CameraShake
|
|
28
|
+
from .draw_rects import DrawHelper
|
|
29
|
+
from .draw_text_helper import DrawTextManager
|
|
30
|
+
from .draw_tool import CrystalDraw
|
|
31
|
+
|
|
32
|
+
# === Misc Helpers ===
|
|
33
|
+
from .fun_helpers import random_name, DebugOverlay
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
# --- Core ---
|
|
38
|
+
"Window", "Sprite", "TileMap", "Player", "Gravity", "FileHelper", "Mathematics",
|
|
39
|
+
|
|
40
|
+
# --- Assets & Animation ---
|
|
41
|
+
"load_image", "load_folder_images", "load_music", "play_music", "Animation",
|
|
42
|
+
|
|
43
|
+
# --- Collision ---
|
|
44
|
+
"check_collision", "resolve_collision",
|
|
45
|
+
|
|
46
|
+
# --- GUI ---
|
|
47
|
+
"Button", "Label", "GUIManager", "random_color", "hex_to_rgb",
|
|
48
|
+
|
|
49
|
+
# --- GUI Extensions ---
|
|
50
|
+
"Toggle", "Slider",
|
|
51
|
+
|
|
52
|
+
# --- Drawing ---
|
|
53
|
+
"gradient_rect", "CameraShake", "DrawHelper", "DrawTextManager", "CrystalDraw",
|
|
54
|
+
|
|
55
|
+
# --- Misc ---
|
|
56
|
+
"random_name", "DebugOverlay",
|
|
57
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Animation:
|
|
2
|
+
def __init__(self, frames, speed=0.1):
|
|
3
|
+
self.frames = frames
|
|
4
|
+
self.speed = speed
|
|
5
|
+
self.current = 0
|
|
6
|
+
self.timer = 0
|
|
7
|
+
|
|
8
|
+
def update(self, dt):
|
|
9
|
+
self.timer += dt
|
|
10
|
+
if self.timer >= self.speed:
|
|
11
|
+
self.timer = 0
|
|
12
|
+
self.current = (self.current + 1) % len(self.frames)
|
|
13
|
+
|
|
14
|
+
def get_frame(self):
|
|
15
|
+
return self.frames[self.current]
|
crystalwindow/assets.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from tkinter import PhotoImage
|
|
3
|
+
|
|
4
|
+
ASSETS = {}
|
|
5
|
+
|
|
6
|
+
def load_image(path, size=None):
|
|
7
|
+
if not os.path.exists(path):
|
|
8
|
+
raise FileNotFoundError(f"Image not found: {path}")
|
|
9
|
+
img = PhotoImage(file=path)
|
|
10
|
+
ASSETS[path] = img
|
|
11
|
+
return img
|
|
12
|
+
|
|
13
|
+
def load_folder_images(folder, nested=True):
|
|
14
|
+
result = {}
|
|
15
|
+
for item in os.listdir(folder):
|
|
16
|
+
full_path = os.path.join(folder, item)
|
|
17
|
+
if os.path.isdir(full_path) and nested:
|
|
18
|
+
result[item] = load_folder_images(full_path, nested=True)
|
|
19
|
+
elif item.lower().endswith((".png", ".gif")): # Tk supports PNG/GIF
|
|
20
|
+
result[item] = load_image(full_path)
|
|
21
|
+
return result
|
|
22
|
+
|
|
23
|
+
def load_music(path):
|
|
24
|
+
"""No-op: Tkinter does not handle music. Placeholder for compatibility."""
|
|
25
|
+
print(f"[assets] Music loading not supported in this current crystalwindow ver sorry! ~Crystal: {path}")
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
def play_music(loop=-1):
|
|
29
|
+
"""No-op: placeholder."""
|
|
30
|
+
print("[assets] Music playback not supported in this current crystalwindow ver sorry! ~Crystal")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
def check_collision(sprite, tiles):
|
|
2
|
+
for t in tiles:
|
|
3
|
+
if sprite.rect.colliderect(t.rect):
|
|
4
|
+
return True
|
|
5
|
+
return False
|
|
6
|
+
|
|
7
|
+
def resolve_collision(sprite, tiles, dy):
|
|
8
|
+
sprite.rect.y += dy
|
|
9
|
+
collided = [t for t in tiles if sprite.rect.colliderect(t.rect)]
|
|
10
|
+
for t in collided:
|
|
11
|
+
if dy > 0:
|
|
12
|
+
sprite.rect.bottom = t.rect.top
|
|
13
|
+
elif dy < 0:
|
|
14
|
+
sprite.rect.top = t.rect.bottom
|
|
15
|
+
sprite.y = sprite.rect.y
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .window import Window
|
|
2
|
+
|
|
3
|
+
def gradient_rect(win, rect, color_start, color_end, vertical=True):
|
|
4
|
+
x,y,w,h = rect
|
|
5
|
+
for i in range(h if vertical else w):
|
|
6
|
+
t = i/(h if vertical else w)
|
|
7
|
+
r = int(color_start[0]*(1-t) + color_end[0]*t)
|
|
8
|
+
g = int(color_start[1]*(1-t) + color_end[1]*t)
|
|
9
|
+
b = int(color_start[2]*(1-t) + color_end[2]*t)
|
|
10
|
+
if vertical:
|
|
11
|
+
win.draw_rect((r,g,b), (x,y+i,w,1))
|
|
12
|
+
else:
|
|
13
|
+
win.draw_rect((r,g,b), (x+i,y,1,h))
|
|
14
|
+
|
|
15
|
+
class CameraShake:
|
|
16
|
+
def __init__(self, intensity=5):
|
|
17
|
+
self.intensity = intensity
|
|
18
|
+
self.offset = (0,0)
|
|
19
|
+
|
|
20
|
+
def update(self):
|
|
21
|
+
import random
|
|
22
|
+
self.offset = (random.randint(-self.intensity,self.intensity),
|
|
23
|
+
random.randint(-self.intensity,self.intensity))
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from crystalwindow import *
|
|
2
|
+
|
|
3
|
+
class DrawHelper:
|
|
4
|
+
def rect(self, win, x, y, w, h, color):
|
|
5
|
+
win.draw_rect(color, (x, y, w, h))
|
|
6
|
+
return self
|
|
7
|
+
|
|
8
|
+
def square(self, win, x, y, size, color):
|
|
9
|
+
win.draw_rect(color, (x, y, size, size))
|
|
10
|
+
return self
|
|
11
|
+
|
|
12
|
+
def circle(self, win, x, y, radius, color):
|
|
13
|
+
win.draw_circle(color, (x, y), radius)
|
|
14
|
+
return self
|
|
15
|
+
|
|
16
|
+
def triangle(self, win, points, color):
|
|
17
|
+
flat_points = [coord for pt in points for coord in pt]
|
|
18
|
+
win.canvas.create_polygon(flat_points, fill=win._to_hex(color))
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
def text(self, win, text, font="Arial", size=16, x=0, y=0, color=(255,255,255), bold=False, cursive=False):
|
|
22
|
+
win.draw_text(text, font=font, size=size, pos=(x, y), color=color, bold=bold, italic=cursive)
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
def gradient_rect(self, win, x, y, w, h, start_color, end_color, vertical=True):
|
|
26
|
+
for i in range(h if vertical else w):
|
|
27
|
+
ratio = i / (h if vertical else w)
|
|
28
|
+
r = int(start_color[0]*(1-ratio) + end_color[0]*ratio)
|
|
29
|
+
g = int(start_color[1]*(1-ratio) + end_color[1]*ratio)
|
|
30
|
+
b = int(start_color[2]*(1-ratio) + end_color[2]*ratio)
|
|
31
|
+
color = f"#{r:02x}{g:02x}{b:02x}"
|
|
32
|
+
if vertical:
|
|
33
|
+
win.canvas.create_line(x, y+i, x+w, y+i, fill=color)
|
|
34
|
+
else:
|
|
35
|
+
win.canvas.create_line(x+i, y, x+i, y+h, fill=color)
|
|
36
|
+
return self
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#draw_text_helper.py
|
|
2
|
+
from .window import Window
|
|
3
|
+
|
|
4
|
+
class DrawTextManager:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.texts = []
|
|
7
|
+
|
|
8
|
+
def add_text(self, text, pos=(0,0), size=16, color=(255,255,255), bold=False, italic=False, duration=None):
|
|
9
|
+
"""
|
|
10
|
+
text: str
|
|
11
|
+
pos: (x,y)
|
|
12
|
+
size: int
|
|
13
|
+
color: RGB tuple
|
|
14
|
+
bold: bool
|
|
15
|
+
italic: bool
|
|
16
|
+
duration: float seconds or None for permanent
|
|
17
|
+
"""
|
|
18
|
+
self.texts.append({
|
|
19
|
+
"text": text,
|
|
20
|
+
"pos": pos,
|
|
21
|
+
"size": size,
|
|
22
|
+
"color": color,
|
|
23
|
+
"bold": bold,
|
|
24
|
+
"italic": italic,
|
|
25
|
+
"duration": duration,
|
|
26
|
+
"timer": 0
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
def update(self, dt):
|
|
30
|
+
# update timers and remove expired text
|
|
31
|
+
for t in self.texts[:]:
|
|
32
|
+
if t["duration"] is not None:
|
|
33
|
+
t["timer"] += dt
|
|
34
|
+
if t["timer"] >= t["duration"]:
|
|
35
|
+
self.texts.remove(t)
|
|
36
|
+
|
|
37
|
+
def draw(self, win: Window):
|
|
38
|
+
for t in self.texts:
|
|
39
|
+
win.draw_text(
|
|
40
|
+
t["text"],
|
|
41
|
+
pos=t["pos"],
|
|
42
|
+
size=t["size"],
|
|
43
|
+
color=t["color"],
|
|
44
|
+
bold=t["bold"],
|
|
45
|
+
italic=t["italic"]
|
|
46
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from .gui import hex_to_rgb
|
|
2
|
+
|
|
3
|
+
class CrystalDraw:
|
|
4
|
+
def __init__(self, win, brush_color="#00aaff", brush_size=8, canvas_rect=None):
|
|
5
|
+
"""
|
|
6
|
+
win: CrystalWindow instance
|
|
7
|
+
brush_color: color string or tuple
|
|
8
|
+
brush_size: int
|
|
9
|
+
canvas_rect: (x, y, w, h) optional drawing area
|
|
10
|
+
"""
|
|
11
|
+
self.win = win
|
|
12
|
+
self.brush_color = hex_to_rgb(brush_color)
|
|
13
|
+
self.brush_size = brush_size
|
|
14
|
+
self.drawing = False
|
|
15
|
+
self.last_pos = None
|
|
16
|
+
self.canvas_rect = canvas_rect or (0, 0, win.width, win.height)
|
|
17
|
+
|
|
18
|
+
def set_color(self, color):
|
|
19
|
+
if isinstance(color, str):
|
|
20
|
+
self.brush_color = hex_to_rgb(color)
|
|
21
|
+
else:
|
|
22
|
+
self.brush_color = color
|
|
23
|
+
|
|
24
|
+
def set_brush_size(self, size):
|
|
25
|
+
self.brush_size = max(1, int(size))
|
|
26
|
+
|
|
27
|
+
def clear(self):
|
|
28
|
+
self.win.fill((255,255,255))
|
|
29
|
+
|
|
30
|
+
def update(self):
|
|
31
|
+
"""Draw if mouse pressed."""
|
|
32
|
+
x, y, w, h = self.canvas_rect
|
|
33
|
+
mx, my = self.win.mouse_pos
|
|
34
|
+
in_bounds = x <= mx <= x+w and y <= my <= y+h
|
|
35
|
+
|
|
36
|
+
if self.win.mouse_pressed(1) and in_bounds:
|
|
37
|
+
if self.last_pos:
|
|
38
|
+
self.win.canvas.create_line(
|
|
39
|
+
self.last_pos[0], self.last_pos[1], mx, my,
|
|
40
|
+
fill=self._to_hex(self.brush_color),
|
|
41
|
+
width=self.brush_size,
|
|
42
|
+
capstyle="round", smooth=True
|
|
43
|
+
)
|
|
44
|
+
self.last_pos = (mx, my)
|
|
45
|
+
else:
|
|
46
|
+
self.last_pos = None
|
|
47
|
+
|
|
48
|
+
def _to_hex(self, color):
|
|
49
|
+
return f"#{color[0]:02x}{color[1]:02x}{color[2]:02x}"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
# random color helpers
|
|
4
|
+
def random_color():
|
|
5
|
+
return (random.randint(0,255), random.randint(0,255), random.randint(0,255))
|
|
6
|
+
|
|
7
|
+
def random_palette(n=5):
|
|
8
|
+
return [random_color() for _ in range(n)]
|
|
9
|
+
|
|
10
|
+
# random name generator
|
|
11
|
+
def random_name():
|
|
12
|
+
syllables = ["ka","zi","lo","ra","mi","to","na","ve"]
|
|
13
|
+
return "".join(random.choice(syllables) for _ in range(3))
|
|
14
|
+
|
|
15
|
+
# tween helper
|
|
16
|
+
def lerp(a, b, t):
|
|
17
|
+
return a + (b - a) * t
|
|
18
|
+
|
|
19
|
+
# debug overlay
|
|
20
|
+
class DebugOverlay:
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.active = True
|
|
23
|
+
|
|
24
|
+
def draw(self, win, fps=60):
|
|
25
|
+
if self.active:
|
|
26
|
+
win.draw_text(f"FPS: {fps}", pos=(10,10))
|
|
27
|
+
mx,my = win.mouse_pos
|
|
28
|
+
win.draw_text(f"Mouse: {mx},{my}", pos=(10,30))
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CrystalWindow Example Demos
|
|
3
|
+
---------------------------
|
|
4
|
+
Run demos like this:
|
|
5
|
+
|
|
6
|
+
python -m CrystalWindow.examples.<demo_name>
|
|
7
|
+
|
|
8
|
+
Available demos:
|
|
9
|
+
guitesting → GUI widgets & layout demo
|
|
10
|
+
gravitytest → Physics & gravity simulation
|
|
11
|
+
windowtesting → Basic window + drawing
|
|
12
|
+
sandbox → Open testbed for experiments
|
|
13
|
+
"""
|
|
14
|
+
__all__ = ["guitesting", "gravitytest", "windowtesting", "sandbox"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# crystalWindow/examples/__main__.py
|
|
2
|
+
import importlib
|
|
3
|
+
|
|
4
|
+
DEMO_SCRIPTS = {
|
|
5
|
+
"guitesting": "GUI widgets & layout demo",
|
|
6
|
+
"gravitytest": "Gravity + physics test",
|
|
7
|
+
"windowtesting": "Basic window and draw test",
|
|
8
|
+
"sandbox": "Free experiment playground",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def list_demos():
|
|
12
|
+
print("CrystalWindow Example Demos 🧊")
|
|
13
|
+
print("--------------------------------")
|
|
14
|
+
for name, desc in DEMO_SCRIPTS.items():
|
|
15
|
+
print(f"{name:<15} - {desc}")
|
|
16
|
+
print("\nRun one with:")
|
|
17
|
+
print(" python -m cystalWindow.examples.<demo_name>\n")
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
list_demos()
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from crystalwindow import *
|
|
2
|
+
|
|
3
|
+
# --- custom player/ball class ---
|
|
4
|
+
class PlayerRect:
|
|
5
|
+
def __init__(self, x, y, w=32, h=32, color=(255,0,0)):
|
|
6
|
+
self.x = x
|
|
7
|
+
self.y = y
|
|
8
|
+
self.width = w
|
|
9
|
+
self.height = h
|
|
10
|
+
self.color = color
|
|
11
|
+
self.vel_y = 0
|
|
12
|
+
|
|
13
|
+
# draw either sprite or rect
|
|
14
|
+
def draw(self, win):
|
|
15
|
+
win.draw_rect(self.color, (self.x, self.y, self.width, self.height))
|
|
16
|
+
|
|
17
|
+
# --- setup window ---
|
|
18
|
+
win = Window(800, 600, "Gravity Sprite/Rect Test")
|
|
19
|
+
|
|
20
|
+
# --- player as colored rect ---
|
|
21
|
+
player = PlayerRect(100, 100, 50, 50, color=(0,255,0))
|
|
22
|
+
|
|
23
|
+
# --- platform as rect ---
|
|
24
|
+
class Platform:
|
|
25
|
+
def __init__(self, x, y, w, h, color=(100,200,100)):
|
|
26
|
+
self.x = x
|
|
27
|
+
self.y = y
|
|
28
|
+
self.width = w
|
|
29
|
+
self.height = h
|
|
30
|
+
self.color = color
|
|
31
|
+
def draw(self, win):
|
|
32
|
+
win.draw_rect(self.color, (self.x, self.y, self.width, self.height))
|
|
33
|
+
|
|
34
|
+
platform = Platform(0, 500, 800, 50)
|
|
35
|
+
|
|
36
|
+
# --- attach gravity ---
|
|
37
|
+
player.gravity = Gravity(player, force=1, bouncy=True, bounce_strength=0.7)
|
|
38
|
+
|
|
39
|
+
# --- main loop ---
|
|
40
|
+
def update(win):
|
|
41
|
+
player.gravity.update(1/60, [platform])
|
|
42
|
+
win.screen.fill((20,20,50))
|
|
43
|
+
player.draw(win)
|
|
44
|
+
platform.draw(win)
|
|
45
|
+
|
|
46
|
+
win.run(update)
|
|
47
|
+
win.quit()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from crystalwindow import *
|
|
2
|
+
|
|
3
|
+
# --- Window setup ---
|
|
4
|
+
win = Window(800, 600, "CrystalWindowLib Mega Sandbox")
|
|
5
|
+
|
|
6
|
+
# --- GUI ---
|
|
7
|
+
gui = GUIManager()
|
|
8
|
+
toggle1 = Toggle((50, 50, 100, 40), value=False)
|
|
9
|
+
slider1 = Slider((50, 120, 200, 30), min_val=0, max_val=100, value=50)
|
|
10
|
+
|
|
11
|
+
btn1 = Button(rect=(50, 200, 150, 50), text="Click Me!", color=random_color(),
|
|
12
|
+
hover_color=random_color(), callback=lambda: print("Button clicked!"))
|
|
13
|
+
lbl1 = Label((250, 50), "Hello GUI!", color=random_color(), size=24)
|
|
14
|
+
|
|
15
|
+
gui.add(toggle1)
|
|
16
|
+
gui.add(slider1)
|
|
17
|
+
gui.add(btn1)
|
|
18
|
+
gui.add(lbl1)
|
|
19
|
+
|
|
20
|
+
# --- Debug Overlay ---
|
|
21
|
+
debug = DebugOverlay()
|
|
22
|
+
|
|
23
|
+
# --- Camera Shake ---
|
|
24
|
+
shake = CameraShake(intensity=20)
|
|
25
|
+
|
|
26
|
+
# --- Main loop ---
|
|
27
|
+
def update(win):
|
|
28
|
+
gui.update(win)
|
|
29
|
+
gui.draw(win)
|
|
30
|
+
|
|
31
|
+
# --- draw text examples ---
|
|
32
|
+
win.draw_text("Normal Text", pos=(400, 50), size=18, color=random_color())
|
|
33
|
+
win.draw_text("Bold Text", pos=(400, 80), size=20, color=random_color(), bold=True)
|
|
34
|
+
win.draw_text("Italic Text", pos=(400, 110), size=20, color=random_color(), italic=True)
|
|
35
|
+
win.draw_text("Bold + Italic", pos=(400, 140), size=22, color=random_color(), bold=True, italic=True)
|
|
36
|
+
|
|
37
|
+
# --- draw toggle/slider values ---
|
|
38
|
+
win.draw_text(f"Toggle: {toggle1.value}", pos=(50, 90), size=18)
|
|
39
|
+
win.draw_text(f"Slider: {int(slider1.value)}", pos=(50, 160), size=18)
|
|
40
|
+
|
|
41
|
+
# --- draw gradient ---
|
|
42
|
+
gradient_rect(win, (50, 300, 200, 100), (255,0,0), (0,0,255))
|
|
43
|
+
|
|
44
|
+
# --- screen shake example (move a rectangle with shake) ---
|
|
45
|
+
shake.update()
|
|
46
|
+
x_off, y_off = shake.offset
|
|
47
|
+
win.draw_rect((0,255,0), (500+x_off, 300+y_off, 100, 50))
|
|
48
|
+
|
|
49
|
+
# --- draw random name + color ---
|
|
50
|
+
win.draw_text(f"Random Name: {random_name()}", pos=(50, 420), size=20, color=random_color())
|
|
51
|
+
|
|
52
|
+
# --- debug overlay ---
|
|
53
|
+
debug.draw(win, fps=int(win.clock.get_fps()))
|
|
54
|
+
|
|
55
|
+
win.run(update)
|
|
56
|
+
win.quit()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from crystalwindow import *
|
|
2
|
+
|
|
3
|
+
# --- setup window ---
|
|
4
|
+
win = Window(800, 600, "CrystalWindow Sandbox")
|
|
5
|
+
|
|
6
|
+
# --- setup debug overlay ---
|
|
7
|
+
debug = DebugOverlay()
|
|
8
|
+
draw = DrawHelper()
|
|
9
|
+
draw.rect(win, 100, 100, 50, 60, (255,0,0)).circle(win, 300, 200, 40, (0,255,0))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# --- player setup ---
|
|
13
|
+
player = Player((100, 400), speed=220)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# --- gravity system ---
|
|
17
|
+
gravity = Gravity(player, force=1.5)
|
|
18
|
+
|
|
19
|
+
# --- GUI setup ---
|
|
20
|
+
|
|
21
|
+
lbl = Label((20, 70), "CrystalWindow Sandbox Ready 🧠")
|
|
22
|
+
|
|
23
|
+
gui = GUIManager()
|
|
24
|
+
gui.add(lbl)
|
|
25
|
+
|
|
26
|
+
# --- rect testing helper ---
|
|
27
|
+
draw = DrawHelper()
|
|
28
|
+
draw.add_rect((300, 500, 200, 50), (50, 200, 255))
|
|
29
|
+
|
|
30
|
+
# --- text system ---
|
|
31
|
+
text_draw = DrawTextManager()
|
|
32
|
+
text_draw.write("Hello Crystal Sandbox!", (250, 50), (255, 255, 255))
|
|
33
|
+
|
|
34
|
+
# --- main loop ---
|
|
35
|
+
running = True
|
|
36
|
+
while running:
|
|
37
|
+
player.update()
|
|
38
|
+
gravity.apply()
|
|
39
|
+
win.fill((20, 20, 30))
|
|
40
|
+
|
|
41
|
+
draw.render()
|
|
42
|
+
player.draw(win)
|
|
43
|
+
gui.draw(win)
|
|
44
|
+
text_draw.render()
|
|
45
|
+
|
|
46
|
+
win.flip()
|
|
47
|
+
|
|
48
|
+
win.run()
|
|
49
|
+
win.quit()
|