clapp-pm 1.0.43__py3-none-any.whl → 1.0.45__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.
- {clapp_pm-1.0.43.data → clapp_pm-1.0.45.data}/data/version.json +1 -1
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/METADATA +1 -1
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/RECORD +12 -24
- package_runner.py +1 -0
- packages/luaozgur-moba/README.md +159 -0
- packages/luaozgur-moba/manifest.json +8 -0
- publish_command.py +41 -39
- version.py +1 -1
- packages/pycloudos/README.md +0 -279
- packages/pycloudos/main.py +0 -193
- packages/pycloudos/manifest.json +0 -15
- packages/pycloudos/rain/__init__.py +0 -7
- packages/pycloudos/rain/dock.py +0 -722
- packages/pycloudos/rain/flet_html_widgets.py +0 -0
- packages/pycloudos/rain/theme.py +0 -930
- packages/pycloudos/rain/ui.py +0 -381
- packages/pycloudos/rain/wallpaper.py +0 -830
- packages/pycloudos/rain/widgets.py +0 -688
- packages/pycloudos/rain/windowmanager.py +0 -605
- packages/pycloudos/requirements-dev.txt +0 -43
- packages/pycloudos/requirements.txt +0 -43
- packages/pycloudos/setup_deps.py +0 -422
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/WHEEL +0 -0
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/entry_points.txt +0 -0
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/licenses/LICENSE +0 -0
- {clapp_pm-1.0.43.dist-info → clapp_pm-1.0.45.dist-info}/top_level.txt +0 -0
packages/pycloudos/rain/theme.py
DELETED
@@ -1,930 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
PyCloud OS Rain Theme
|
3
|
-
Arayüz bileşenleri için görsel temalar ve ikon paketleri yönetimi
|
4
|
-
"""
|
5
|
-
|
6
|
-
import os
|
7
|
-
import json
|
8
|
-
import logging
|
9
|
-
from pathlib import Path
|
10
|
-
from typing import Dict, List, Optional, Any, Callable
|
11
|
-
from dataclasses import dataclass, asdict
|
12
|
-
from enum import Enum
|
13
|
-
|
14
|
-
try:
|
15
|
-
from PyQt6.QtGui import QColor, QPalette, QFont, QPixmap, QIcon
|
16
|
-
from PyQt6.QtCore import Qt
|
17
|
-
from PyQt6.QtWidgets import QApplication
|
18
|
-
PYQT_AVAILABLE = True
|
19
|
-
except ImportError:
|
20
|
-
PYQT_AVAILABLE = False
|
21
|
-
|
22
|
-
class ThemeMode(Enum):
|
23
|
-
"""Tema modları"""
|
24
|
-
LIGHT = "light"
|
25
|
-
DARK = "dark"
|
26
|
-
AUTO = "auto"
|
27
|
-
|
28
|
-
class IconTheme(Enum):
|
29
|
-
"""İkon temaları"""
|
30
|
-
SYSTEM = "system"
|
31
|
-
FLAT = "flat"
|
32
|
-
MODERN = "modern"
|
33
|
-
CLASSIC = "classic"
|
34
|
-
|
35
|
-
@dataclass
|
36
|
-
class ColorScheme:
|
37
|
-
"""Renk şeması"""
|
38
|
-
primary: str = "#2196F3"
|
39
|
-
secondary: str = "#FFC107"
|
40
|
-
success: str = "#4CAF50"
|
41
|
-
warning: str = "#FF9800"
|
42
|
-
error: str = "#F44336"
|
43
|
-
info: str = "#00BCD4"
|
44
|
-
|
45
|
-
# Arka plan renkleri
|
46
|
-
background: str = "#FFFFFF"
|
47
|
-
surface: str = "#F5F5F5"
|
48
|
-
card: str = "#FFFFFF"
|
49
|
-
|
50
|
-
# Metin renkleri
|
51
|
-
text_primary: str = "#212121"
|
52
|
-
text_secondary: str = "#757575"
|
53
|
-
text_disabled: str = "#BDBDBD"
|
54
|
-
|
55
|
-
# Sınır ve ayırıcı renkleri
|
56
|
-
border: str = "#E0E0E0"
|
57
|
-
divider: str = "#E0E0E0"
|
58
|
-
|
59
|
-
def to_dict(self) -> Dict:
|
60
|
-
"""Dict'e çevir"""
|
61
|
-
return asdict(self)
|
62
|
-
|
63
|
-
@classmethod
|
64
|
-
def from_dict(cls, data: Dict) -> 'ColorScheme':
|
65
|
-
"""Dict'ten oluştur"""
|
66
|
-
return cls(**data)
|
67
|
-
|
68
|
-
@dataclass
|
69
|
-
class ThemeConfig:
|
70
|
-
"""Tema yapılandırması"""
|
71
|
-
name: str
|
72
|
-
display_name: str
|
73
|
-
mode: ThemeMode
|
74
|
-
colors: ColorScheme
|
75
|
-
fonts: Dict[str, str] = None
|
76
|
-
borders: Dict[str, str] = None
|
77
|
-
shadows: Dict[str, str] = None
|
78
|
-
animations: Dict[str, Any] = None
|
79
|
-
|
80
|
-
def __post_init__(self):
|
81
|
-
if self.fonts is None:
|
82
|
-
self.fonts = {
|
83
|
-
"default": "Arial",
|
84
|
-
"heading": "Arial",
|
85
|
-
"monospace": "Courier New",
|
86
|
-
"size_small": "9",
|
87
|
-
"size_normal": "10",
|
88
|
-
"size_large": "12",
|
89
|
-
"size_heading": "14"
|
90
|
-
}
|
91
|
-
|
92
|
-
if self.borders is None:
|
93
|
-
self.borders = {
|
94
|
-
"radius": "4px",
|
95
|
-
"width": "1px",
|
96
|
-
"style": "solid"
|
97
|
-
}
|
98
|
-
|
99
|
-
if self.shadows is None:
|
100
|
-
self.shadows = {
|
101
|
-
"small": "0 1px 3px rgba(0,0,0,0.12)",
|
102
|
-
"medium": "0 4px 6px rgba(0,0,0,0.15)",
|
103
|
-
"large": "0 10px 20px rgba(0,0,0,0.19)"
|
104
|
-
}
|
105
|
-
|
106
|
-
if self.animations is None:
|
107
|
-
self.animations = {
|
108
|
-
"duration_fast": 150,
|
109
|
-
"duration_normal": 250,
|
110
|
-
"duration_slow": 400,
|
111
|
-
"easing": "ease-in-out"
|
112
|
-
}
|
113
|
-
|
114
|
-
def to_dict(self) -> Dict:
|
115
|
-
"""Dict'e çevir"""
|
116
|
-
data = asdict(self)
|
117
|
-
data['mode'] = self.mode.value
|
118
|
-
data['colors'] = self.colors.to_dict()
|
119
|
-
return data
|
120
|
-
|
121
|
-
@classmethod
|
122
|
-
def from_dict(cls, data: Dict) -> 'ThemeConfig':
|
123
|
-
"""Dict'ten oluştur"""
|
124
|
-
data['mode'] = ThemeMode(data.get('mode', 'auto'))
|
125
|
-
data['colors'] = ColorScheme.from_dict(data.get('colors', {}))
|
126
|
-
return cls(**data)
|
127
|
-
|
128
|
-
class IconManager:
|
129
|
-
"""İkon yöneticisi"""
|
130
|
-
|
131
|
-
def __init__(self, theme_manager=None):
|
132
|
-
self.theme_manager = theme_manager
|
133
|
-
self.logger = logging.getLogger("IconManager")
|
134
|
-
|
135
|
-
# İkon dizinleri
|
136
|
-
self.system_icons_dir = Path("system/icons")
|
137
|
-
self.user_icons_dir = Path("users/icons")
|
138
|
-
self.system_icons_dir.mkdir(parents=True, exist_ok=True)
|
139
|
-
self.user_icons_dir.mkdir(parents=True, exist_ok=True)
|
140
|
-
|
141
|
-
# İkon cache
|
142
|
-
self.icon_cache: Dict[str, Any] = {}
|
143
|
-
self.icon_mappings: Dict[str, str] = {}
|
144
|
-
|
145
|
-
# Mevcut tema
|
146
|
-
self.current_theme = IconTheme.SYSTEM
|
147
|
-
|
148
|
-
# Başlangıç
|
149
|
-
self.load_icon_mappings()
|
150
|
-
self.create_default_icons()
|
151
|
-
|
152
|
-
def load_icon_mappings(self):
|
153
|
-
"""İkon eşleştirmelerini yükle"""
|
154
|
-
try:
|
155
|
-
mappings_file = self.system_icons_dir / "mappings.json"
|
156
|
-
|
157
|
-
if mappings_file.exists():
|
158
|
-
with open(mappings_file, 'r', encoding='utf-8') as f:
|
159
|
-
self.icon_mappings = json.load(f)
|
160
|
-
else:
|
161
|
-
self.create_default_mappings()
|
162
|
-
|
163
|
-
except Exception as e:
|
164
|
-
self.logger.error(f"Failed to load icon mappings: {e}")
|
165
|
-
|
166
|
-
def create_default_mappings(self):
|
167
|
-
"""Varsayılan ikon eşleştirmeleri oluştur"""
|
168
|
-
default_mappings = {
|
169
|
-
# Dosya türleri
|
170
|
-
"file.txt": "📄",
|
171
|
-
"file.py": "🐍",
|
172
|
-
"file.js": "📜",
|
173
|
-
"file.html": "🌐",
|
174
|
-
"file.css": "🎨",
|
175
|
-
"file.json": "📋",
|
176
|
-
"file.md": "📝",
|
177
|
-
"file.pdf": "📕",
|
178
|
-
"file.doc": "📘",
|
179
|
-
"file.xls": "📊",
|
180
|
-
"file.zip": "📦",
|
181
|
-
"file.rar": "📦",
|
182
|
-
"file.exe": "⚙️",
|
183
|
-
"file.app": "📱",
|
184
|
-
"file.dmg": "💿",
|
185
|
-
"file.iso": "💿",
|
186
|
-
|
187
|
-
# Klasörler
|
188
|
-
"folder": "📁",
|
189
|
-
"folder.open": "📂",
|
190
|
-
"folder.home": "🏠",
|
191
|
-
"folder.documents": "📑",
|
192
|
-
"folder.downloads": "⬇️",
|
193
|
-
"folder.pictures": "🖼️",
|
194
|
-
"folder.music": "🎵",
|
195
|
-
"folder.videos": "🎬",
|
196
|
-
"folder.desktop": "🖥️",
|
197
|
-
"folder.trash": "🗑️",
|
198
|
-
|
199
|
-
# Uygulamalar
|
200
|
-
"app.files": "📁",
|
201
|
-
"app.terminal": "💻",
|
202
|
-
"app.settings": "⚙️",
|
203
|
-
"app.browser": "🌐",
|
204
|
-
"app.editor": "📝",
|
205
|
-
"app.calculator": "🔢",
|
206
|
-
"app.calendar": "📅",
|
207
|
-
"app.clock": "🕐",
|
208
|
-
"app.notes": "📓",
|
209
|
-
"app.weather": "🌤️",
|
210
|
-
|
211
|
-
# Sistem
|
212
|
-
"system.warning": "⚠️",
|
213
|
-
"system.error": "❌",
|
214
|
-
"system.info": "ℹ️",
|
215
|
-
"system.success": "✅",
|
216
|
-
"system.loading": "⏳",
|
217
|
-
"system.refresh": "🔄",
|
218
|
-
"system.search": "🔍",
|
219
|
-
"system.menu": "☰",
|
220
|
-
"system.close": "✕",
|
221
|
-
"system.minimize": "−",
|
222
|
-
"system.maximize": "□",
|
223
|
-
|
224
|
-
# Eylemler
|
225
|
-
"action.copy": "📋",
|
226
|
-
"action.cut": "✂️",
|
227
|
-
"action.paste": "📄",
|
228
|
-
"action.delete": "🗑️",
|
229
|
-
"action.rename": "✏️",
|
230
|
-
"action.new": "📄",
|
231
|
-
"action.save": "💾",
|
232
|
-
"action.open": "📂",
|
233
|
-
"action.print": "🖨️",
|
234
|
-
"action.share": "📤",
|
235
|
-
|
236
|
-
# Navigasyon
|
237
|
-
"nav.back": "⬅️",
|
238
|
-
"nav.forward": "➡️",
|
239
|
-
"nav.up": "⬆️",
|
240
|
-
"nav.down": "⬇️",
|
241
|
-
"nav.home": "🏠",
|
242
|
-
"nav.refresh": "🔄",
|
243
|
-
}
|
244
|
-
|
245
|
-
try:
|
246
|
-
mappings_file = self.system_icons_dir / "mappings.json"
|
247
|
-
with open(mappings_file, 'w', encoding='utf-8') as f:
|
248
|
-
json.dump(default_mappings, f, indent=2, ensure_ascii=False)
|
249
|
-
|
250
|
-
self.icon_mappings = default_mappings
|
251
|
-
self.logger.info("Default icon mappings created")
|
252
|
-
|
253
|
-
except Exception as e:
|
254
|
-
self.logger.error(f"Failed to create default mappings: {e}")
|
255
|
-
|
256
|
-
def create_default_icons(self):
|
257
|
-
"""Varsayılan ikonları oluştur (emoji tabanlı)"""
|
258
|
-
# Emoji tabanlı ikonlar zaten mappings'te var
|
259
|
-
# Bu metod gelecekte gerçek ikon dosyaları için kullanılabilir
|
260
|
-
pass
|
261
|
-
|
262
|
-
def get_icon(self, icon_id: str, size: int = 16) -> Any:
|
263
|
-
"""İkon al"""
|
264
|
-
try:
|
265
|
-
# Cache'de var mı?
|
266
|
-
cache_key = f"{icon_id}_{size}_{self.current_theme.value}"
|
267
|
-
if cache_key in self.icon_cache:
|
268
|
-
return self.icon_cache[cache_key]
|
269
|
-
|
270
|
-
# Emoji ikon
|
271
|
-
if icon_id in self.icon_mappings:
|
272
|
-
emoji = self.icon_mappings[icon_id]
|
273
|
-
|
274
|
-
if PYQT_AVAILABLE:
|
275
|
-
# PyQt6 için emoji'yi QIcon'a çevir
|
276
|
-
pixmap = QPixmap(size, size)
|
277
|
-
pixmap.fill(Qt.GlobalColor.transparent)
|
278
|
-
|
279
|
-
# Bu basit bir implementasyon, gerçek uygulamada
|
280
|
-
# emoji'yi bitmap'e çeviren bir kütüphane kullanılmalı
|
281
|
-
icon = QIcon()
|
282
|
-
self.icon_cache[cache_key] = icon
|
283
|
-
return icon
|
284
|
-
else:
|
285
|
-
# PyQt yok, emoji string döndür
|
286
|
-
self.icon_cache[cache_key] = emoji
|
287
|
-
return emoji
|
288
|
-
|
289
|
-
# Dosya ikonu ara
|
290
|
-
icon_file = self.get_icon_file(icon_id, size)
|
291
|
-
if icon_file and icon_file.exists():
|
292
|
-
if PYQT_AVAILABLE:
|
293
|
-
icon = QIcon(str(icon_file))
|
294
|
-
self.icon_cache[cache_key] = icon
|
295
|
-
return icon
|
296
|
-
else:
|
297
|
-
self.icon_cache[cache_key] = str(icon_file)
|
298
|
-
return str(icon_file)
|
299
|
-
|
300
|
-
# Varsayılan ikon
|
301
|
-
default_icon = self.icon_mappings.get("file", "📄")
|
302
|
-
self.icon_cache[cache_key] = default_icon
|
303
|
-
return default_icon
|
304
|
-
|
305
|
-
except Exception as e:
|
306
|
-
self.logger.error(f"Failed to get icon {icon_id}: {e}")
|
307
|
-
return "📄" # Varsayılan
|
308
|
-
|
309
|
-
def get_icon_file(self, icon_id: str, size: int = 16) -> Optional[Path]:
|
310
|
-
"""İkon dosya yolunu al"""
|
311
|
-
# Theme klasörlerinde ara
|
312
|
-
theme_dirs = [
|
313
|
-
self.system_icons_dir / self.current_theme.value,
|
314
|
-
self.system_icons_dir / "default",
|
315
|
-
self.user_icons_dir
|
316
|
-
]
|
317
|
-
|
318
|
-
for theme_dir in theme_dirs:
|
319
|
-
if not theme_dir.exists():
|
320
|
-
continue
|
321
|
-
|
322
|
-
# Farklı dosya formatlarını dene
|
323
|
-
for ext in ['.png', '.svg', '.ico', '.jpg']:
|
324
|
-
icon_file = theme_dir / f"{icon_id}_{size}{ext}"
|
325
|
-
if icon_file.exists():
|
326
|
-
return icon_file
|
327
|
-
|
328
|
-
# Boyut belirtilmemiş dosya
|
329
|
-
icon_file = theme_dir / f"{icon_id}{ext}"
|
330
|
-
if icon_file.exists():
|
331
|
-
return icon_file
|
332
|
-
|
333
|
-
return None
|
334
|
-
|
335
|
-
def get_file_icon(self, file_path: str) -> Any:
|
336
|
-
"""Dosya tipine göre ikon al"""
|
337
|
-
path = Path(file_path)
|
338
|
-
|
339
|
-
if path.is_dir():
|
340
|
-
# Özel klasör isimleri
|
341
|
-
folder_names = {
|
342
|
-
"Desktop": "folder.desktop",
|
343
|
-
"Documents": "folder.documents",
|
344
|
-
"Downloads": "folder.downloads",
|
345
|
-
"Pictures": "folder.pictures",
|
346
|
-
"Music": "folder.music",
|
347
|
-
"Videos": "folder.videos",
|
348
|
-
"Trash": "folder.trash"
|
349
|
-
}
|
350
|
-
|
351
|
-
folder_icon = folder_names.get(path.name, "folder")
|
352
|
-
return self.get_icon(folder_icon)
|
353
|
-
else:
|
354
|
-
# Dosya uzantısına göre
|
355
|
-
extension = path.suffix.lower()
|
356
|
-
icon_id = f"file{extension}" if extension else "file"
|
357
|
-
|
358
|
-
return self.get_icon(icon_id)
|
359
|
-
|
360
|
-
def set_icon_theme(self, theme: IconTheme):
|
361
|
-
"""İkon temasını değiştir"""
|
362
|
-
if theme != self.current_theme:
|
363
|
-
self.current_theme = theme
|
364
|
-
self.icon_cache.clear() # Cache'i temizle
|
365
|
-
self.logger.info(f"Icon theme changed to: {theme.value}")
|
366
|
-
|
367
|
-
def add_custom_icon(self, icon_id: str, icon_path: str) -> bool:
|
368
|
-
"""Özel ikon ekle"""
|
369
|
-
try:
|
370
|
-
source_path = Path(icon_path)
|
371
|
-
if not source_path.exists():
|
372
|
-
return False
|
373
|
-
|
374
|
-
# Kullanıcı ikon dizinine kopyala
|
375
|
-
target_path = self.user_icons_dir / f"{icon_id}{source_path.suffix}"
|
376
|
-
|
377
|
-
import shutil
|
378
|
-
shutil.copy2(source_path, target_path)
|
379
|
-
|
380
|
-
# Cache'i temizle
|
381
|
-
cache_keys_to_remove = [key for key in self.icon_cache.keys()
|
382
|
-
if key.startswith(icon_id)]
|
383
|
-
for key in cache_keys_to_remove:
|
384
|
-
del self.icon_cache[key]
|
385
|
-
|
386
|
-
self.logger.info(f"Custom icon added: {icon_id}")
|
387
|
-
return True
|
388
|
-
|
389
|
-
except Exception as e:
|
390
|
-
self.logger.error(f"Failed to add custom icon {icon_id}: {e}")
|
391
|
-
return False
|
392
|
-
|
393
|
-
class ThemeManager:
|
394
|
-
"""Ana tema yöneticisi"""
|
395
|
-
|
396
|
-
def __init__(self, kernel=None):
|
397
|
-
self.kernel = kernel
|
398
|
-
self.logger = logging.getLogger("ThemeManager")
|
399
|
-
|
400
|
-
# Tema dizinleri
|
401
|
-
self.themes_dir = Path("system/themes")
|
402
|
-
self.user_themes_dir = Path("users/themes")
|
403
|
-
self.themes_dir.mkdir(parents=True, exist_ok=True)
|
404
|
-
self.user_themes_dir.mkdir(parents=True, exist_ok=True)
|
405
|
-
|
406
|
-
# Tema verileri
|
407
|
-
self.themes: Dict[str, ThemeConfig] = {}
|
408
|
-
self.current_theme_name = "dark"
|
409
|
-
self.current_mode = ThemeMode.AUTO
|
410
|
-
|
411
|
-
# İkon yöneticisi
|
412
|
-
self.icon_manager = IconManager(self)
|
413
|
-
|
414
|
-
# Tema değişim callback'leri
|
415
|
-
self.theme_callbacks: List[Callable] = []
|
416
|
-
|
417
|
-
# Başlangıç
|
418
|
-
self.create_default_themes()
|
419
|
-
self.load_themes()
|
420
|
-
self.load_current_theme()
|
421
|
-
|
422
|
-
def create_default_themes(self):
|
423
|
-
"""Varsayılan temaları oluştur"""
|
424
|
-
# Koyu tema
|
425
|
-
dark_colors = ColorScheme(
|
426
|
-
primary="#2196F3",
|
427
|
-
secondary="#FFC107",
|
428
|
-
success="#4CAF50",
|
429
|
-
warning="#FF9800",
|
430
|
-
error="#F44336",
|
431
|
-
info="#00BCD4",
|
432
|
-
background="#1e1e1e",
|
433
|
-
surface="#2d2d2d",
|
434
|
-
card="#3c3c3c",
|
435
|
-
text_primary="#ffffff",
|
436
|
-
text_secondary="#b0b0b0",
|
437
|
-
text_disabled="#666666",
|
438
|
-
border="#555555",
|
439
|
-
divider="#444444"
|
440
|
-
)
|
441
|
-
|
442
|
-
dark_theme = ThemeConfig(
|
443
|
-
name="dark",
|
444
|
-
display_name="Koyu Tema",
|
445
|
-
mode=ThemeMode.DARK,
|
446
|
-
colors=dark_colors
|
447
|
-
)
|
448
|
-
|
449
|
-
# Açık tema
|
450
|
-
light_colors = ColorScheme(
|
451
|
-
primary="#2196F3",
|
452
|
-
secondary="#FFC107",
|
453
|
-
success="#4CAF50",
|
454
|
-
warning="#FF9800",
|
455
|
-
error="#F44336",
|
456
|
-
info="#00BCD4",
|
457
|
-
background="#ffffff",
|
458
|
-
surface="#f5f5f5",
|
459
|
-
card="#ffffff",
|
460
|
-
text_primary="#212121",
|
461
|
-
text_secondary="#757575",
|
462
|
-
text_disabled="#bdbdbd",
|
463
|
-
border="#e0e0e0",
|
464
|
-
divider="#e0e0e0"
|
465
|
-
)
|
466
|
-
|
467
|
-
light_theme = ThemeConfig(
|
468
|
-
name="light",
|
469
|
-
display_name="Açık Tema",
|
470
|
-
mode=ThemeMode.LIGHT,
|
471
|
-
colors=light_colors
|
472
|
-
)
|
473
|
-
|
474
|
-
# Temaları kaydet
|
475
|
-
themes = {"dark": dark_theme, "light": light_theme}
|
476
|
-
|
477
|
-
for theme_name, theme in themes.items():
|
478
|
-
theme_file = self.themes_dir / f"{theme_name}.json"
|
479
|
-
try:
|
480
|
-
with open(theme_file, 'w', encoding='utf-8') as f:
|
481
|
-
json.dump(theme.to_dict(), f, indent=2, ensure_ascii=False)
|
482
|
-
except Exception as e:
|
483
|
-
self.logger.error(f"Failed to save theme {theme_name}: {e}")
|
484
|
-
|
485
|
-
def load_themes(self):
|
486
|
-
"""Temaları yükle"""
|
487
|
-
try:
|
488
|
-
# Sistem temaları
|
489
|
-
for theme_file in self.themes_dir.glob("*.json"):
|
490
|
-
try:
|
491
|
-
with open(theme_file, 'r', encoding='utf-8') as f:
|
492
|
-
theme_data = json.load(f)
|
493
|
-
|
494
|
-
theme = ThemeConfig.from_dict(theme_data)
|
495
|
-
self.themes[theme.name] = theme
|
496
|
-
|
497
|
-
except Exception as e:
|
498
|
-
self.logger.error(f"Failed to load theme {theme_file}: {e}")
|
499
|
-
|
500
|
-
# Kullanıcı temaları
|
501
|
-
for theme_file in self.user_themes_dir.glob("*.json"):
|
502
|
-
try:
|
503
|
-
with open(theme_file, 'r', encoding='utf-8') as f:
|
504
|
-
theme_data = json.load(f)
|
505
|
-
|
506
|
-
theme = ThemeConfig.from_dict(theme_data)
|
507
|
-
self.themes[theme.name] = theme
|
508
|
-
|
509
|
-
except Exception as e:
|
510
|
-
self.logger.error(f"Failed to load user theme {theme_file}: {e}")
|
511
|
-
|
512
|
-
self.logger.info(f"Loaded {len(self.themes)} themes")
|
513
|
-
|
514
|
-
except Exception as e:
|
515
|
-
self.logger.error(f"Failed to load themes: {e}")
|
516
|
-
|
517
|
-
def load_current_theme(self):
|
518
|
-
"""Mevcut temayı yükle"""
|
519
|
-
try:
|
520
|
-
if self.kernel:
|
521
|
-
config_manager = self.kernel.get_module("config")
|
522
|
-
if config_manager:
|
523
|
-
self.current_theme_name = config_manager.get("ui.theme", "dark")
|
524
|
-
mode_str = config_manager.get("ui.theme_mode", "auto")
|
525
|
-
self.current_mode = ThemeMode(mode_str)
|
526
|
-
|
527
|
-
# Otomatik mod kontrolü
|
528
|
-
if self.current_mode == ThemeMode.AUTO:
|
529
|
-
# Sistem saatine göre koyu/açık tema seç
|
530
|
-
from datetime import datetime
|
531
|
-
hour = datetime.now().hour
|
532
|
-
if 6 <= hour <= 18: # Gündüz
|
533
|
-
auto_theme = "light"
|
534
|
-
else: # Gece
|
535
|
-
auto_theme = "dark"
|
536
|
-
|
537
|
-
if auto_theme in self.themes:
|
538
|
-
self.current_theme_name = auto_theme
|
539
|
-
|
540
|
-
self.logger.info(f"Current theme: {self.current_theme_name} (mode: {self.current_mode.value})")
|
541
|
-
|
542
|
-
except Exception as e:
|
543
|
-
self.logger.error(f"Failed to load current theme: {e}")
|
544
|
-
|
545
|
-
def get_current_theme(self) -> Optional[ThemeConfig]:
|
546
|
-
"""Mevcut temayı al"""
|
547
|
-
return self.themes.get(self.current_theme_name)
|
548
|
-
|
549
|
-
def set_theme(self, theme_name: str, save: bool = True) -> bool:
|
550
|
-
"""Tema değiştir"""
|
551
|
-
try:
|
552
|
-
if theme_name not in self.themes:
|
553
|
-
self.logger.warning(f"Theme not found: {theme_name}")
|
554
|
-
return False
|
555
|
-
|
556
|
-
old_theme = self.current_theme_name
|
557
|
-
self.current_theme_name = theme_name
|
558
|
-
|
559
|
-
# Config'e kaydet
|
560
|
-
if save and self.kernel:
|
561
|
-
config_manager = self.kernel.get_module("config")
|
562
|
-
if config_manager:
|
563
|
-
config_manager.set("ui.theme", theme_name)
|
564
|
-
|
565
|
-
# Tema uygula
|
566
|
-
self.apply_current_theme()
|
567
|
-
|
568
|
-
# Callback'leri çağır
|
569
|
-
self.call_theme_callbacks(old_theme, theme_name)
|
570
|
-
|
571
|
-
self.logger.info(f"Theme changed from {old_theme} to {theme_name}")
|
572
|
-
return True
|
573
|
-
|
574
|
-
except Exception as e:
|
575
|
-
self.logger.error(f"Failed to set theme {theme_name}: {e}")
|
576
|
-
return False
|
577
|
-
|
578
|
-
def set_theme_mode(self, mode: ThemeMode, save: bool = True) -> bool:
|
579
|
-
"""Tema modunu değiştir"""
|
580
|
-
try:
|
581
|
-
old_mode = self.current_mode
|
582
|
-
self.current_mode = mode
|
583
|
-
|
584
|
-
# Config'e kaydet
|
585
|
-
if save and self.kernel:
|
586
|
-
config_manager = self.kernel.get_module("config")
|
587
|
-
if config_manager:
|
588
|
-
config_manager.set("ui.theme_mode", mode.value)
|
589
|
-
|
590
|
-
# Mevcut temayı yeniden yükle
|
591
|
-
self.load_current_theme()
|
592
|
-
self.apply_current_theme()
|
593
|
-
|
594
|
-
self.logger.info(f"Theme mode changed from {old_mode.value} to {mode.value}")
|
595
|
-
return True
|
596
|
-
|
597
|
-
except Exception as e:
|
598
|
-
self.logger.error(f"Failed to set theme mode {mode.value}: {e}")
|
599
|
-
return False
|
600
|
-
|
601
|
-
def apply_current_theme(self):
|
602
|
-
"""Mevcut temayı uygula"""
|
603
|
-
try:
|
604
|
-
theme = self.get_current_theme()
|
605
|
-
if not theme or not PYQT_AVAILABLE:
|
606
|
-
return
|
607
|
-
|
608
|
-
app = QApplication.instance()
|
609
|
-
if not app:
|
610
|
-
return
|
611
|
-
|
612
|
-
# Renk paleti oluştur
|
613
|
-
palette = QPalette()
|
614
|
-
|
615
|
-
# Ana renkler
|
616
|
-
palette.setColor(QPalette.ColorRole.Window, QColor(theme.colors.background))
|
617
|
-
palette.setColor(QPalette.ColorRole.WindowText, QColor(theme.colors.text_primary))
|
618
|
-
palette.setColor(QPalette.ColorRole.Base, QColor(theme.colors.surface))
|
619
|
-
palette.setColor(QPalette.ColorRole.AlternateBase, QColor(theme.colors.card))
|
620
|
-
palette.setColor(QPalette.ColorRole.Text, QColor(theme.colors.text_primary))
|
621
|
-
palette.setColor(QPalette.ColorRole.Button, QColor(theme.colors.surface))
|
622
|
-
palette.setColor(QPalette.ColorRole.ButtonText, QColor(theme.colors.text_primary))
|
623
|
-
palette.setColor(QPalette.ColorRole.Highlight, QColor(theme.colors.primary))
|
624
|
-
palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#ffffff"))
|
625
|
-
|
626
|
-
# Devre dışı renkler
|
627
|
-
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor(theme.colors.text_disabled))
|
628
|
-
palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor(theme.colors.text_disabled))
|
629
|
-
|
630
|
-
# Paleti uygula
|
631
|
-
app.setPalette(palette)
|
632
|
-
|
633
|
-
# Global stylesheet
|
634
|
-
stylesheet = self.generate_stylesheet(theme)
|
635
|
-
app.setStyleSheet(stylesheet)
|
636
|
-
|
637
|
-
self.logger.debug(f"Theme applied: {theme.name}")
|
638
|
-
|
639
|
-
except Exception as e:
|
640
|
-
self.logger.error(f"Failed to apply theme: {e}")
|
641
|
-
|
642
|
-
def generate_stylesheet(self, theme: ThemeConfig) -> str:
|
643
|
-
"""Tema için stylesheet oluştur"""
|
644
|
-
colors = theme.colors
|
645
|
-
fonts = theme.fonts
|
646
|
-
borders = theme.borders
|
647
|
-
|
648
|
-
stylesheet = f"""
|
649
|
-
/* Genel stil */
|
650
|
-
QWidget {{
|
651
|
-
background-color: {colors.background};
|
652
|
-
color: {colors.text_primary};
|
653
|
-
font-family: '{fonts['default']}';
|
654
|
-
font-size: {fonts['size_normal']}pt;
|
655
|
-
}}
|
656
|
-
|
657
|
-
/* Butonlar */
|
658
|
-
QPushButton {{
|
659
|
-
background-color: {colors.surface};
|
660
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
661
|
-
border-radius: {borders['radius']};
|
662
|
-
padding: 6px 12px;
|
663
|
-
min-height: 20px;
|
664
|
-
}}
|
665
|
-
|
666
|
-
QPushButton:hover {{
|
667
|
-
background-color: {colors.primary};
|
668
|
-
color: white;
|
669
|
-
}}
|
670
|
-
|
671
|
-
QPushButton:pressed {{
|
672
|
-
background-color: {colors.primary};
|
673
|
-
border: {borders['width']} {borders['style']} {colors.primary};
|
674
|
-
}}
|
675
|
-
|
676
|
-
QPushButton:disabled {{
|
677
|
-
background-color: {colors.surface};
|
678
|
-
color: {colors.text_disabled};
|
679
|
-
border-color: {colors.text_disabled};
|
680
|
-
}}
|
681
|
-
|
682
|
-
/* Metin kutuları */
|
683
|
-
QLineEdit, QTextEdit, QPlainTextEdit {{
|
684
|
-
background-color: {colors.card};
|
685
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
686
|
-
border-radius: {borders['radius']};
|
687
|
-
padding: 4px 8px;
|
688
|
-
selection-background-color: {colors.primary};
|
689
|
-
}}
|
690
|
-
|
691
|
-
QLineEdit:focus, QTextEdit:focus, QPlainTextEdit:focus {{
|
692
|
-
border-color: {colors.primary};
|
693
|
-
}}
|
694
|
-
|
695
|
-
/* Listeler */
|
696
|
-
QListWidget, QTreeWidget {{
|
697
|
-
background-color: {colors.card};
|
698
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
699
|
-
border-radius: {borders['radius']};
|
700
|
-
alternate-background-color: {colors.surface};
|
701
|
-
}}
|
702
|
-
|
703
|
-
QListWidget::item, QTreeWidget::item {{
|
704
|
-
padding: 4px;
|
705
|
-
border-bottom: 1px solid {colors.divider};
|
706
|
-
}}
|
707
|
-
|
708
|
-
QListWidget::item:selected, QTreeWidget::item:selected {{
|
709
|
-
background-color: {colors.primary};
|
710
|
-
color: white;
|
711
|
-
}}
|
712
|
-
|
713
|
-
QListWidget::item:hover, QTreeWidget::item:hover {{
|
714
|
-
background-color: {colors.surface};
|
715
|
-
}}
|
716
|
-
|
717
|
-
/* Menüler */
|
718
|
-
QMenuBar {{
|
719
|
-
background-color: {colors.surface};
|
720
|
-
border-bottom: 1px solid {colors.border};
|
721
|
-
}}
|
722
|
-
|
723
|
-
QMenuBar::item {{
|
724
|
-
background-color: transparent;
|
725
|
-
padding: 4px 8px;
|
726
|
-
}}
|
727
|
-
|
728
|
-
QMenuBar::item:selected {{
|
729
|
-
background-color: {colors.primary};
|
730
|
-
color: white;
|
731
|
-
}}
|
732
|
-
|
733
|
-
QMenu {{
|
734
|
-
background-color: {colors.card};
|
735
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
736
|
-
border-radius: {borders['radius']};
|
737
|
-
}}
|
738
|
-
|
739
|
-
QMenu::item {{
|
740
|
-
padding: 6px 12px;
|
741
|
-
}}
|
742
|
-
|
743
|
-
QMenu::item:selected {{
|
744
|
-
background-color: {colors.primary};
|
745
|
-
color: white;
|
746
|
-
}}
|
747
|
-
|
748
|
-
/* Scroll barlar */
|
749
|
-
QScrollBar:vertical {{
|
750
|
-
background-color: {colors.surface};
|
751
|
-
width: 12px;
|
752
|
-
border-radius: 6px;
|
753
|
-
}}
|
754
|
-
|
755
|
-
QScrollBar::handle:vertical {{
|
756
|
-
background-color: {colors.text_secondary};
|
757
|
-
border-radius: 6px;
|
758
|
-
min-height: 20px;
|
759
|
-
}}
|
760
|
-
|
761
|
-
QScrollBar::handle:vertical:hover {{
|
762
|
-
background-color: {colors.primary};
|
763
|
-
}}
|
764
|
-
|
765
|
-
/* Tab widget */
|
766
|
-
QTabWidget::pane {{
|
767
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
768
|
-
border-radius: {borders['radius']};
|
769
|
-
background-color: {colors.card};
|
770
|
-
}}
|
771
|
-
|
772
|
-
QTabBar::tab {{
|
773
|
-
background-color: {colors.surface};
|
774
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
775
|
-
padding: 6px 12px;
|
776
|
-
margin-right: 2px;
|
777
|
-
}}
|
778
|
-
|
779
|
-
QTabBar::tab:selected {{
|
780
|
-
background-color: {colors.primary};
|
781
|
-
color: white;
|
782
|
-
}}
|
783
|
-
|
784
|
-
QTabBar::tab:hover {{
|
785
|
-
background-color: {colors.text_secondary};
|
786
|
-
}}
|
787
|
-
|
788
|
-
/* Başlık çubukları */
|
789
|
-
QFrame[class="title-bar"] {{
|
790
|
-
background-color: {colors.surface};
|
791
|
-
border-bottom: 1px solid {colors.border};
|
792
|
-
}}
|
793
|
-
|
794
|
-
/* Dock */
|
795
|
-
QFrame[class="dock"] {{
|
796
|
-
background-color: {colors.surface};
|
797
|
-
border: {borders['width']} {borders['style']} {colors.border};
|
798
|
-
border-radius: 8px;
|
799
|
-
}}
|
800
|
-
|
801
|
-
/* Topbar */
|
802
|
-
QFrame[class="topbar"] {{
|
803
|
-
background-color: {colors.surface};
|
804
|
-
border-bottom: 1px solid {colors.border};
|
805
|
-
}}
|
806
|
-
"""
|
807
|
-
|
808
|
-
return stylesheet
|
809
|
-
|
810
|
-
def get_available_themes(self) -> List[Dict]:
|
811
|
-
"""Mevcut temaları al"""
|
812
|
-
themes_list = []
|
813
|
-
for theme_name, theme in self.themes.items():
|
814
|
-
themes_list.append({
|
815
|
-
"name": theme.name,
|
816
|
-
"display_name": theme.display_name,
|
817
|
-
"mode": theme.mode.value,
|
818
|
-
"is_current": theme_name == self.current_theme_name
|
819
|
-
})
|
820
|
-
|
821
|
-
return themes_list
|
822
|
-
|
823
|
-
def add_theme_callback(self, callback: Callable):
|
824
|
-
"""Tema değişim callback'i ekle"""
|
825
|
-
self.theme_callbacks.append(callback)
|
826
|
-
|
827
|
-
def remove_theme_callback(self, callback: Callable):
|
828
|
-
"""Tema değişim callback'ini kaldır"""
|
829
|
-
if callback in self.theme_callbacks:
|
830
|
-
self.theme_callbacks.remove(callback)
|
831
|
-
|
832
|
-
def call_theme_callbacks(self, old_theme: str, new_theme: str):
|
833
|
-
"""Tema callback'lerini çağır"""
|
834
|
-
for callback in self.theme_callbacks:
|
835
|
-
try:
|
836
|
-
callback(old_theme, new_theme)
|
837
|
-
except Exception as e:
|
838
|
-
self.logger.error(f"Theme callback failed: {e}")
|
839
|
-
|
840
|
-
def export_theme(self, theme_name: str, export_path: str) -> bool:
|
841
|
-
"""Temayı dışa aktar"""
|
842
|
-
try:
|
843
|
-
if theme_name not in self.themes:
|
844
|
-
return False
|
845
|
-
|
846
|
-
theme = self.themes[theme_name]
|
847
|
-
export_file = Path(export_path)
|
848
|
-
|
849
|
-
with open(export_file, 'w', encoding='utf-8') as f:
|
850
|
-
json.dump(theme.to_dict(), f, indent=2, ensure_ascii=False)
|
851
|
-
|
852
|
-
self.logger.info(f"Theme exported: {theme_name} to {export_path}")
|
853
|
-
return True
|
854
|
-
|
855
|
-
except Exception as e:
|
856
|
-
self.logger.error(f"Failed to export theme {theme_name}: {e}")
|
857
|
-
return False
|
858
|
-
|
859
|
-
def import_theme(self, import_path: str) -> bool:
|
860
|
-
"""Tema içe aktar"""
|
861
|
-
try:
|
862
|
-
import_file = Path(import_path)
|
863
|
-
if not import_file.exists():
|
864
|
-
return False
|
865
|
-
|
866
|
-
with open(import_file, 'r', encoding='utf-8') as f:
|
867
|
-
theme_data = json.load(f)
|
868
|
-
|
869
|
-
theme = ThemeConfig.from_dict(theme_data)
|
870
|
-
|
871
|
-
# Kullanıcı tema dizinine kaydet
|
872
|
-
theme_file = self.user_themes_dir / f"{theme.name}.json"
|
873
|
-
with open(theme_file, 'w', encoding='utf-8') as f:
|
874
|
-
json.dump(theme.to_dict(), f, indent=2, ensure_ascii=False)
|
875
|
-
|
876
|
-
# Tema listesine ekle
|
877
|
-
self.themes[theme.name] = theme
|
878
|
-
|
879
|
-
self.logger.info(f"Theme imported: {theme.name}")
|
880
|
-
return True
|
881
|
-
|
882
|
-
except Exception as e:
|
883
|
-
self.logger.error(f"Failed to import theme: {e}")
|
884
|
-
return False
|
885
|
-
|
886
|
-
def shutdown(self):
|
887
|
-
"""Theme manager'ı kapat"""
|
888
|
-
try:
|
889
|
-
# Mevcut ayarları kaydet
|
890
|
-
if self.kernel:
|
891
|
-
config_manager = self.kernel.get_module("config")
|
892
|
-
if config_manager:
|
893
|
-
config_manager.set("ui.theme", self.current_theme_name)
|
894
|
-
config_manager.set("ui.theme_mode", self.current_mode.value)
|
895
|
-
|
896
|
-
self.logger.info("Theme manager shutdown completed")
|
897
|
-
|
898
|
-
except Exception as e:
|
899
|
-
self.logger.error(f"Theme manager shutdown failed: {e}")
|
900
|
-
|
901
|
-
# Kolaylık fonksiyonları
|
902
|
-
_theme_manager = None
|
903
|
-
|
904
|
-
def init_theme_manager(kernel=None) -> ThemeManager:
|
905
|
-
"""Theme manager'ı başlat"""
|
906
|
-
global _theme_manager
|
907
|
-
_theme_manager = ThemeManager(kernel)
|
908
|
-
return _theme_manager
|
909
|
-
|
910
|
-
def get_theme_manager() -> Optional[ThemeManager]:
|
911
|
-
"""Theme manager'ı al"""
|
912
|
-
return _theme_manager
|
913
|
-
|
914
|
-
def get_icon(icon_id: str, size: int = 16) -> Any:
|
915
|
-
"""İkon al (kısayol)"""
|
916
|
-
if _theme_manager:
|
917
|
-
return _theme_manager.icon_manager.get_icon(icon_id, size)
|
918
|
-
return "📄"
|
919
|
-
|
920
|
-
def get_file_icon(file_path: str) -> Any:
|
921
|
-
"""Dosya ikonu al (kısayol)"""
|
922
|
-
if _theme_manager:
|
923
|
-
return _theme_manager.icon_manager.get_file_icon(file_path)
|
924
|
-
return "📄"
|
925
|
-
|
926
|
-
def set_theme(theme_name: str) -> bool:
|
927
|
-
"""Tema değiştir (kısayol)"""
|
928
|
-
if _theme_manager:
|
929
|
-
return _theme_manager.set_theme(theme_name)
|
930
|
-
return False
|