clapp-pm 1.0.41__py3-none-any.whl → 1.0.43__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.
Files changed (33) hide show
  1. {clapp_pm-1.0.41.data → clapp_pm-1.0.43.data}/data/version.json +1 -1
  2. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/METADATA +1 -1
  3. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/RECORD +33 -9
  4. packages/cloud-calc/README.md +93 -0
  5. packages/cloud-calc/clapp-packages-repo/README.md +46 -0
  6. packages/cloud-calc/clapp-packages-repo/index.json +40 -0
  7. packages/cloud-calc/clapp-packages-repo/packages.json +40 -0
  8. packages/cloud-calc/main.py +364 -0
  9. packages/cloud-calc/manifest.json +8 -0
  10. packages/cloud-calc/requirements.txt +1 -0
  11. packages/pycloudos/README.md +279 -0
  12. packages/pycloudos/main.py +193 -0
  13. packages/pycloudos/manifest.json +15 -0
  14. packages/pycloudos/pycloud_fs/home/Desktop/test_desktop_file.txt +11 -0
  15. packages/pycloudos/pycloud_fs/home/default/Desktop/test_dosya.txt +1 -0
  16. packages/pycloudos/pycloud_fs/home/default/Desktop/test_script.py +11 -0
  17. packages/pycloudos/rain/__init__.py +7 -0
  18. packages/pycloudos/rain/dock.py +722 -0
  19. packages/pycloudos/rain/flet_html_widgets.py +0 -0
  20. packages/pycloudos/rain/theme.py +930 -0
  21. packages/pycloudos/rain/ui.py +381 -0
  22. packages/pycloudos/rain/wallpaper.py +830 -0
  23. packages/pycloudos/rain/widgets.py +688 -0
  24. packages/pycloudos/rain/windowmanager.py +605 -0
  25. packages/pycloudos/requirements-dev.txt +43 -0
  26. packages/pycloudos/requirements.txt +43 -0
  27. packages/pycloudos/setup_deps.py +422 -0
  28. publish_command.py +32 -44
  29. version.py +1 -1
  30. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/WHEEL +0 -0
  31. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/entry_points.txt +0 -0
  32. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/licenses/LICENSE +0 -0
  33. {clapp_pm-1.0.41.dist-info → clapp_pm-1.0.43.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,722 @@
1
+ """
2
+ Rain Dock - Modern Dock Sistemi
3
+ Kullanıcıya sabit uygulamalara hızlı erişim sağlayan modern dock yapısı
4
+ macOS Big Sur/Monterey tarzında glassmorphism efektleri ile
5
+ """
6
+
7
+ import logging
8
+ import math
9
+ from typing import List, Dict, Optional
10
+ from pathlib import Path
11
+
12
+ try:
13
+ from PyQt6.QtWidgets import (QWidget, QHBoxLayout, QVBoxLayout, QLabel,
14
+ QPushButton, QFrame, QScrollArea, QMenu,
15
+ QGraphicsDropShadowEffect, QSizePolicy, QGraphicsBlurEffect)
16
+ from PyQt6.QtCore import (Qt, QSize, QPropertyAnimation, QEasingCurve, pyqtSignal,
17
+ QTimer, QRect, QPoint, QParallelAnimationGroup, QSequentialAnimationGroup)
18
+ from PyQt6.QtGui import (QFont, QPixmap, QAction, QPainter, QColor, QBrush, QIcon,
19
+ QPainterPath, QLinearGradient, QRadialGradient, QPen)
20
+ PYQT_AVAILABLE = True
21
+ except ImportError:
22
+ PYQT_AVAILABLE = False
23
+
24
+ class ModernDockIcon(QPushButton):
25
+ """Modern Dock simgesi widget'ı - glassmorphism efektleri ile"""
26
+
27
+ # Sinyaller
28
+ app_launch_requested = pyqtSignal(str) # app_id
29
+ context_menu_requested = pyqtSignal(str, object) # app_id, position
30
+
31
+ def __init__(self, app_id: str, app_name: str, icon_path: str = None, icon_text: str = "📱", is_running: bool = False):
32
+ super().__init__()
33
+
34
+ self.app_id = app_id
35
+ self.app_name = app_name
36
+ self.icon_path = icon_path
37
+ self.icon_text = icon_text
38
+ self.is_running = is_running
39
+ self.is_hovered = False
40
+
41
+ # Animasyon değişkenleri
42
+ self.base_size = 64
43
+ self.hover_size = 80
44
+ self.current_scale = 1.0
45
+
46
+ self.setup_ui()
47
+ self.setup_animations()
48
+ self.setup_effects()
49
+
50
+ def setup_ui(self):
51
+ """UI kurulumu"""
52
+ self.setFixedSize(self.base_size, self.base_size)
53
+ self.setObjectName("ModernDockIcon")
54
+
55
+ # İkon yükleme - daha yüksek kalite
56
+ if self.icon_path and Path(self.icon_path).exists():
57
+ # PNG ikon kullan
58
+ pixmap = QPixmap(self.icon_path)
59
+ if not pixmap.isNull():
60
+ # İkonu ultra yüksek kalitede boyutlandır
61
+ icon_size = int(self.base_size * 0.75) # %75 boyut
62
+ scaled_pixmap = pixmap.scaled(
63
+ icon_size, icon_size,
64
+ Qt.AspectRatioMode.KeepAspectRatio,
65
+ Qt.TransformationMode.SmoothTransformation
66
+ )
67
+
68
+ # Şeffaf arka plan ile temiz ikon oluştur
69
+ clean_pixmap = QPixmap(icon_size, icon_size)
70
+ clean_pixmap.fill(Qt.GlobalColor.transparent)
71
+
72
+ painter = QPainter(clean_pixmap)
73
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
74
+ painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform)
75
+
76
+ # İkonu ortala
77
+ x = (icon_size - scaled_pixmap.width()) // 2
78
+ y = (icon_size - scaled_pixmap.height()) // 2
79
+ painter.drawPixmap(x, y, scaled_pixmap)
80
+ painter.end()
81
+
82
+ self.setIcon(QIcon(clean_pixmap))
83
+ self.setIconSize(QSize(icon_size, icon_size))
84
+
85
+ # Metin temizle (sadece ikon göster)
86
+ self.setText("")
87
+ else:
88
+ # Fallback: emoji
89
+ self.setText(self.icon_text)
90
+ self.setStyleSheet("font-size: 32px;")
91
+ else:
92
+ # Fallback: emoji veya metin
93
+ self.setText(self.icon_text)
94
+ self.setStyleSheet("font-size: 32px;")
95
+
96
+ # Modern stil
97
+ self.setStyleSheet(self._get_modern_style())
98
+
99
+ # Tooltip
100
+ self.setToolTip(self.app_name)
101
+
102
+ def _get_modern_style(self) -> str:
103
+ """Modern glassmorphism stil"""
104
+ return f"""
105
+ QPushButton {{
106
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
107
+ stop:0 rgba(255, 255, 255, 0.3),
108
+ stop:0.5 rgba(255, 255, 255, 0.2),
109
+ stop:1 rgba(255, 255, 255, 0.1));
110
+ border: 1px solid rgba(255, 255, 255, 0.3);
111
+ border-radius: {self.base_size // 4}px;
112
+ margin: 2px;
113
+ padding: 0px;
114
+ }}
115
+
116
+ QPushButton:hover {{
117
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
118
+ stop:0 rgba(255, 255, 255, 0.4),
119
+ stop:0.5 rgba(255, 255, 255, 0.25),
120
+ stop:1 rgba(255, 255, 255, 0.15));
121
+ border: 1px solid rgba(255, 255, 255, 0.5);
122
+ }}
123
+
124
+ QPushButton:pressed {{
125
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
126
+ stop:0 rgba(255, 255, 255, 0.2),
127
+ stop:0.5 rgba(255, 255, 255, 0.1),
128
+ stop:1 rgba(255, 255, 255, 0.05));
129
+ border: 1px solid rgba(255, 255, 255, 0.4);
130
+ }}
131
+ """
132
+
133
+ def setup_animations(self):
134
+ """Modern animasyonları kur"""
135
+ # Hover scale animasyonu
136
+ self.scale_animation = QPropertyAnimation(self, b"geometry")
137
+ self.scale_animation.setDuration(200)
138
+ self.scale_animation.setEasingCurve(QEasingCurve.Type.OutCubic)
139
+
140
+ # Bounce animasyonu
141
+ self.bounce_animation = QSequentialAnimationGroup()
142
+
143
+ # Yukarı bounce
144
+ bounce_up = QPropertyAnimation(self, b"geometry")
145
+ bounce_up.setDuration(150)
146
+ bounce_up.setEasingCurve(QEasingCurve.Type.OutQuad)
147
+
148
+ # Aşağı bounce
149
+ bounce_down = QPropertyAnimation(self, b"geometry")
150
+ bounce_down.setDuration(150)
151
+ bounce_down.setEasingCurve(QEasingCurve.Type.InQuad)
152
+
153
+ self.bounce_animation.addAnimation(bounce_up)
154
+ self.bounce_animation.addAnimation(bounce_down)
155
+
156
+ def setup_effects(self):
157
+ """Görsel efektleri kur"""
158
+ # Gölge efekti
159
+ self.shadow_effect = QGraphicsDropShadowEffect()
160
+ self.shadow_effect.setBlurRadius(15)
161
+ self.shadow_effect.setColor(QColor(0, 0, 0, 80))
162
+ self.shadow_effect.setOffset(0, 4)
163
+ self.setGraphicsEffect(self.shadow_effect)
164
+
165
+ def enterEvent(self, event):
166
+ """Mouse hover başlangıcı - devre dışı"""
167
+ super().enterEvent(event)
168
+ # Hover efekti kaldırıldı
169
+ pass
170
+
171
+ def leaveEvent(self, event):
172
+ """Mouse hover bitişi - devre dışı"""
173
+ super().leaveEvent(event)
174
+ # Hover efekti kaldırıldı
175
+ pass
176
+
177
+ def animate_hover(self, hover: bool):
178
+ """Hover animasyonu - devre dışı bırakıldı"""
179
+ # Hover efekti tamamen kaldırıldı
180
+ # Sadece tıklama animasyonu aktif
181
+ pass
182
+
183
+ def animate_click(self):
184
+ """Tıklama animasyonu - yukarı zıplama efekti"""
185
+ if self.bounce_animation.state() == QSequentialAnimationGroup.State.Running:
186
+ return
187
+
188
+ current_rect = self.geometry()
189
+
190
+ # Daha belirgin yukarı zıplama (20px yukarı)
191
+ bounce_height = 20
192
+ bounce_up_rect = QRect(
193
+ current_rect.x(),
194
+ current_rect.y() - bounce_height,
195
+ current_rect.width(),
196
+ current_rect.height()
197
+ )
198
+
199
+ # Aşağı bounce (orijinal pozisyon)
200
+ bounce_down_rect = current_rect
201
+
202
+ # Animasyonları ayarla
203
+ bounce_up = self.bounce_animation.animationAt(0)
204
+ bounce_down = self.bounce_animation.animationAt(1)
205
+
206
+ # Yukarı zıplama - hızlı ve etkili
207
+ bounce_up.setDuration(120) # Daha hızlı
208
+ bounce_up.setEasingCurve(QEasingCurve.Type.OutQuart) # Daha etkili easing
209
+ bounce_up.setStartValue(current_rect)
210
+ bounce_up.setEndValue(bounce_up_rect)
211
+
212
+ # Aşağı zıplama - yumuşak iniş
213
+ bounce_down.setDuration(180) # Biraz daha yavaş iniş
214
+ bounce_down.setEasingCurve(QEasingCurve.Type.InQuart) # Yumuşak iniş
215
+ bounce_down.setStartValue(bounce_up_rect)
216
+ bounce_down.setEndValue(bounce_down_rect)
217
+
218
+ self.bounce_animation.start()
219
+
220
+ def set_running_state(self, is_running: bool):
221
+ """Çalışma durumunu ayarla"""
222
+ if self.is_running != is_running:
223
+ self.is_running = is_running
224
+ self.update_running_indicator()
225
+
226
+ def update_running_indicator(self):
227
+ """Çalışma göstergesini güncelle"""
228
+ # Bu özellik paintEvent'te implement edilecek
229
+ self.update()
230
+
231
+ def paintEvent(self, event):
232
+ """Özel çizim - çalışma göstergesi için"""
233
+ super().paintEvent(event)
234
+
235
+ if self.is_running:
236
+ painter = QPainter(self)
237
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
238
+
239
+ # Çalışma göstergesi (alt kısımda küçük nokta)
240
+ indicator_size = 6
241
+ indicator_x = (self.width() - indicator_size) // 2
242
+ indicator_y = self.height() - 8
243
+
244
+ # Gradient nokta
245
+ gradient = QRadialGradient(
246
+ indicator_x + indicator_size // 2,
247
+ indicator_y + indicator_size // 2,
248
+ indicator_size // 2
249
+ )
250
+ gradient.setColorAt(0, QColor(255, 255, 255, 255))
251
+ gradient.setColorAt(1, QColor(200, 200, 200, 200))
252
+
253
+ painter.setBrush(QBrush(gradient))
254
+ painter.setPen(Qt.PenStyle.NoPen)
255
+ painter.drawEllipse(indicator_x, indicator_y, indicator_size, indicator_size)
256
+
257
+ painter.end()
258
+
259
+ def mousePressEvent(self, event):
260
+ """Fare tıklama olayı"""
261
+ if event.button() == Qt.MouseButton.LeftButton:
262
+ self.animate_click()
263
+ self.app_launch_requested.emit(self.app_id)
264
+ elif event.button() == Qt.MouseButton.RightButton:
265
+ # PyQt6'da globalPosition() kullan
266
+ try:
267
+ global_pos = event.globalPosition().toPoint()
268
+ except AttributeError:
269
+ # Fallback
270
+ global_pos = self.mapToGlobal(event.pos())
271
+
272
+ self.context_menu_requested.emit(self.app_id, global_pos)
273
+
274
+ super().mousePressEvent(event)
275
+
276
+ class ModernRainDock(QWidget):
277
+ """Modern Rain UI Dock bileşeni - glassmorphism efektleri ile"""
278
+
279
+ def __init__(self, kernel):
280
+ super().__init__()
281
+ self.kernel = kernel
282
+ self.logger = logging.getLogger("ModernRainDock")
283
+
284
+ if not PYQT_AVAILABLE:
285
+ return
286
+
287
+ self.dock_icons: List[ModernDockIcon] = []
288
+ self.pinned_apps: List[Dict] = []
289
+ self.running_apps: List[str] = []
290
+
291
+ # Dock pozisyon ve boyut ayarları
292
+ self.dock_height = 80
293
+ self.dock_margin = 20
294
+ self.icon_spacing = 12
295
+
296
+ self.setup_ui()
297
+ self.load_pinned_apps()
298
+ self.setup_connections()
299
+ self.setup_update_timer()
300
+
301
+ def setup_ui(self):
302
+ """Modern arayüzü kur"""
303
+ self.setFixedHeight(self.dock_height + self.dock_margin)
304
+ self.setStyleSheet("""
305
+ QWidget {
306
+ background-color: transparent;
307
+ border: none;
308
+ }
309
+ """)
310
+
311
+ # Ana layout
312
+ main_layout = QHBoxLayout(self)
313
+ main_layout.setContentsMargins(0, 0, 0, self.dock_margin)
314
+ main_layout.setSpacing(0)
315
+
316
+ # Sol spacer
317
+ main_layout.addStretch()
318
+
319
+ # Modern dock container
320
+ self.dock_container = QWidget()
321
+ self.dock_layout = QHBoxLayout(self.dock_container)
322
+ self.dock_layout.setSpacing(self.icon_spacing)
323
+ self.dock_layout.setContentsMargins(20, 10, 20, 10)
324
+
325
+ # Modern glassmorphism dock container stili
326
+ self.dock_container.setStyleSheet(f"""
327
+ QWidget {{
328
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
329
+ stop:0 rgba(255, 255, 255, 0.2),
330
+ stop:0.5 rgba(255, 255, 255, 0.15),
331
+ stop:1 rgba(255, 255, 255, 0.1));
332
+ border: 1px solid rgba(255, 255, 255, 0.3);
333
+ border-radius: {self.dock_height // 3}px;
334
+ }}
335
+ """)
336
+
337
+ # Modern gölge efekti
338
+ shadow_effect = QGraphicsDropShadowEffect()
339
+ shadow_effect.setBlurRadius(25)
340
+ shadow_effect.setColor(QColor(0, 0, 0, 60))
341
+ shadow_effect.setOffset(0, 8)
342
+ self.dock_container.setGraphicsEffect(shadow_effect)
343
+
344
+ main_layout.addWidget(self.dock_container)
345
+
346
+ # Sağ spacer
347
+ main_layout.addStretch()
348
+
349
+ def load_pinned_apps(self):
350
+ """Sabitlenmiş uygulamaları yükle"""
351
+ try:
352
+ # Varsayılan sabitlenmiş uygulamalar
353
+ default_apps = [
354
+ {"id": "cloud_files", "name": "Dosyalar", "icon": "📁"},
355
+ {"id": "cloud_terminal", "name": "Terminal", "icon": "💻"},
356
+ {"id": "cloud_notepad", "name": "Notepad", "icon": "📝"},
357
+ {"id": "cloud_browser", "name": "Tarayıcı", "icon": "🌐"},
358
+ {"id": "cloud_pyide", "name": "Python IDE", "icon": "🐍"},
359
+ ]
360
+
361
+ # AppExplorer'dan gerçek uygulama bilgilerini al
362
+ if self.kernel:
363
+ app_explorer = self.kernel.get_module("appexplorer")
364
+ if app_explorer:
365
+ for app_config in default_apps:
366
+ app_info = app_explorer.get_app_by_id(app_config["id"])
367
+ if app_info:
368
+ # PNG ikon yolunu al
369
+ icon_path = None
370
+ if app_info.app_path:
371
+ potential_icon = Path(app_info.app_path) / "icon.png"
372
+ if potential_icon.exists():
373
+ icon_path = str(potential_icon)
374
+
375
+ self.pinned_apps.append({
376
+ "id": app_config["id"],
377
+ "name": app_info.name,
378
+ "icon": app_config["icon"],
379
+ "icon_path": icon_path
380
+ })
381
+ else:
382
+ # Fallback
383
+ self.pinned_apps.append(app_config)
384
+ else:
385
+ self.pinned_apps = default_apps
386
+ else:
387
+ self.pinned_apps = default_apps
388
+
389
+ self.update_dock_icons()
390
+
391
+ except Exception as e:
392
+ self.logger.error(f"Failed to load pinned apps: {e}")
393
+
394
+ def update_dock_icons(self):
395
+ """Dock simgelerini güncelle"""
396
+ # Mevcut simgeleri temizle
397
+ for icon in self.dock_icons:
398
+ icon.setParent(None)
399
+ icon.deleteLater()
400
+
401
+ self.dock_icons.clear()
402
+
403
+ # Yeni simgeleri oluştur
404
+ for app_info in self.pinned_apps:
405
+ is_running = app_info["id"] in self.running_apps
406
+
407
+ icon = ModernDockIcon(
408
+ app_id=app_info["id"],
409
+ app_name=app_info["name"],
410
+ icon_path=app_info.get("icon_path"),
411
+ icon_text=app_info["icon"],
412
+ is_running=is_running
413
+ )
414
+
415
+ # Sinyal bağlantıları
416
+ icon.app_launch_requested.connect(self.launch_app)
417
+ icon.context_menu_requested.connect(self.show_app_context_menu)
418
+
419
+ self.dock_layout.addWidget(icon)
420
+ self.dock_icons.append(icon)
421
+
422
+ self.logger.info(f"Updated dock with {len(self.dock_icons)} icons")
423
+
424
+ def setup_connections(self):
425
+ """Sinyal bağlantıları"""
426
+ if self.kernel:
427
+ # Event sistemi ile bağlantı kur
428
+ events = self.kernel.get_module("events")
429
+ if events:
430
+ events.subscribe("app_launched", self.on_app_launched)
431
+ events.subscribe("app_closed", self.on_app_closed)
432
+
433
+ def setup_update_timer(self):
434
+ """Güncelleme zamanlayıcısı"""
435
+ self.update_timer = QTimer()
436
+ self.update_timer.timeout.connect(self.update_running_apps)
437
+ self.update_timer.start(2000) # Her 2 saniyede güncelle
438
+
439
+ def update_running_apps(self):
440
+ """Çalışan uygulamaları güncelle"""
441
+ try:
442
+ if self.kernel:
443
+ launcher = self.kernel.get_module("launcher")
444
+ if launcher:
445
+ running_apps = list(launcher.get_all_running_apps().keys())
446
+
447
+ if running_apps != self.running_apps:
448
+ self.running_apps = running_apps
449
+ self.update_dock_running_states()
450
+
451
+ except Exception as e:
452
+ self.logger.error(f"Failed to update running apps: {e}")
453
+
454
+ def update_dock_running_states(self):
455
+ """Dock simgelerinin çalışma durumlarını güncelle"""
456
+ for icon in self.dock_icons:
457
+ is_running = icon.app_id in self.running_apps
458
+ icon.set_running_state(is_running)
459
+
460
+ def launch_app(self, app_id: str):
461
+ """Uygulama başlat"""
462
+ try:
463
+ self.logger.info(f"Launching app: {app_id}")
464
+
465
+ if self.kernel:
466
+ launcher = self.kernel.get_module("launcher")
467
+ if launcher:
468
+ success = launcher.launch_app(app_id)
469
+ if success:
470
+ self.logger.info(f"App {app_id} launch requested successfully")
471
+ else:
472
+ self.logger.warning(f"Failed to launch app {app_id}")
473
+ self._fallback_launch(app_id)
474
+ else:
475
+ self.logger.warning("Launcher module not available")
476
+ self._fallback_launch(app_id)
477
+ else:
478
+ self.logger.warning("Kernel not available")
479
+ self._fallback_launch(app_id)
480
+
481
+ except Exception as e:
482
+ self.logger.error(f"Failed to launch app {app_id}: {e}")
483
+ self._fallback_launch(app_id)
484
+
485
+ def _fallback_launch(self, app_id: str):
486
+ """Fallback uygulama başlatma"""
487
+ try:
488
+ import subprocess
489
+ import os
490
+
491
+ # Uygulama dizinini bul
492
+ app_path = Path("apps") / app_id
493
+ if app_path.exists():
494
+ main_py = app_path / "main.py"
495
+ if main_py.exists():
496
+ # Python ile başlat
497
+ subprocess.Popen([
498
+ "python3", str(main_py)
499
+ ], cwd=str(app_path))
500
+
501
+ self.logger.info(f"Fallback launch successful for {app_id}")
502
+ else:
503
+ self.logger.error(f"main.py not found for {app_id}")
504
+ else:
505
+ self.logger.error(f"App directory not found: {app_path}")
506
+
507
+ except Exception as e:
508
+ self.logger.error(f"Fallback launch failed for {app_id}: {e}")
509
+
510
+ def show_app_context_menu(self, app_id: str, position):
511
+ """Uygulama bağlam menüsünü göster"""
512
+ try:
513
+ self.logger.info(f"Showing context menu for app: {app_id}")
514
+
515
+ menu = QMenu(self)
516
+ menu.setStyleSheet("""
517
+ QMenu {
518
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
519
+ stop:0 rgba(45, 45, 45, 0.95),
520
+ stop:1 rgba(35, 35, 35, 0.95));
521
+ border: 1px solid rgba(80, 80, 80, 0.8);
522
+ border-radius: 12px;
523
+ padding: 8px;
524
+ color: #ffffff;
525
+ }
526
+
527
+ QMenu::item {
528
+ background-color: transparent;
529
+ padding: 12px 20px;
530
+ border-radius: 8px;
531
+ margin: 2px;
532
+ color: #ffffff;
533
+ font-size: 14px;
534
+ }
535
+
536
+ QMenu::item:selected {
537
+ background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
538
+ stop:0 rgba(70, 70, 70, 0.8),
539
+ stop:1 rgba(60, 60, 60, 0.8));
540
+ color: #ffffff;
541
+ }
542
+
543
+ QMenu::separator {
544
+ height: 1px;
545
+ background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
546
+ stop:0 transparent,
547
+ stop:0.5 rgba(100, 100, 100, 0.6),
548
+ stop:1 transparent);
549
+ margin: 6px 16px;
550
+ }
551
+ """)
552
+
553
+ # Menü öğeleri
554
+ if app_id in self.running_apps:
555
+ # Çalışan uygulama menüsü
556
+ show_action = QAction("🔍 Göster", self)
557
+ show_action.triggered.connect(lambda: self.show_app(app_id))
558
+ menu.addAction(show_action)
559
+
560
+ close_action = QAction("❌ Kapat", self)
561
+ close_action.triggered.connect(lambda: self.close_app(app_id))
562
+ menu.addAction(close_action)
563
+
564
+ menu.addSeparator()
565
+
566
+ restart_action = QAction("🔄 Yeniden Başlat", self)
567
+ restart_action.triggered.connect(lambda: self.restart_app(app_id))
568
+ menu.addAction(restart_action)
569
+ else:
570
+ # Çalışmayan uygulama menüsü
571
+ launch_action = QAction("🚀 Başlat", self)
572
+ launch_action.triggered.connect(lambda: self.launch_app(app_id))
573
+ menu.addAction(launch_action)
574
+
575
+ menu.addSeparator()
576
+
577
+ # Dock işlemleri
578
+ unpin_action = QAction("📌 Dock'tan Kaldır", self)
579
+ unpin_action.triggered.connect(lambda: self.unpin_app(app_id))
580
+ menu.addAction(unpin_action)
581
+
582
+ info_action = QAction("ℹ️ Uygulama Bilgileri", self)
583
+ info_action.triggered.connect(lambda: self.show_app_info(app_id))
584
+ menu.addAction(info_action)
585
+
586
+ menu.exec(position)
587
+
588
+ except Exception as e:
589
+ self.logger.error(f"Failed to show context menu: {e}")
590
+ import traceback
591
+ self.logger.error(traceback.format_exc())
592
+
593
+ def show_app(self, app_id: str):
594
+ """Uygulamayı göster/öne getir"""
595
+ try:
596
+ if self.kernel:
597
+ window_manager = self.kernel.get_module("windowmanager")
598
+ if window_manager:
599
+ windows = window_manager.get_windows_by_app(app_id)
600
+ if windows:
601
+ window_manager.focus_window(windows[0].window_id)
602
+ except Exception as e:
603
+ self.logger.error(f"Failed to show app {app_id}: {e}")
604
+
605
+ def close_app(self, app_id: str):
606
+ """Uygulamayı kapat"""
607
+ try:
608
+ if self.kernel:
609
+ launcher = self.kernel.get_module("launcher")
610
+ if launcher:
611
+ launcher.stop_app(app_id)
612
+ except Exception as e:
613
+ self.logger.error(f"Failed to close app {app_id}: {e}")
614
+
615
+ def restart_app(self, app_id: str):
616
+ """Uygulamayı yeniden başlat"""
617
+ try:
618
+ if self.kernel:
619
+ launcher = self.kernel.get_module("launcher")
620
+ if launcher:
621
+ launcher.restart_app(app_id)
622
+ except Exception as e:
623
+ self.logger.error(f"Failed to restart app {app_id}: {e}")
624
+
625
+ def unpin_app(self, app_id: str):
626
+ """Uygulamayı dock'tan kaldır"""
627
+ try:
628
+ self.pinned_apps = [app for app in self.pinned_apps if app["id"] != app_id]
629
+ self.update_dock_icons()
630
+ self.logger.info(f"Unpinned app: {app_id}")
631
+ except Exception as e:
632
+ self.logger.error(f"Failed to unpin app {app_id}: {e}")
633
+
634
+ def pin_app(self, app_id: str, app_name: str, icon: str):
635
+ """Uygulamayı dock'a sabitle"""
636
+ try:
637
+ # Zaten sabitlenmiş mi kontrol et
638
+ if any(app["id"] == app_id for app in self.pinned_apps):
639
+ return
640
+
641
+ # PNG ikon yolunu bul
642
+ icon_path = None
643
+ if self.kernel:
644
+ app_explorer = self.kernel.get_module("appexplorer")
645
+ if app_explorer:
646
+ app_info = app_explorer.get_app_by_id(app_id)
647
+ if app_info and app_info.app_path:
648
+ potential_icon = Path(app_info.app_path) / "icon.png"
649
+ if potential_icon.exists():
650
+ icon_path = str(potential_icon)
651
+
652
+ self.pinned_apps.append({
653
+ "id": app_id,
654
+ "name": app_name,
655
+ "icon": icon,
656
+ "icon_path": icon_path
657
+ })
658
+
659
+ self.update_dock_icons()
660
+ self.logger.info(f"Pinned app: {app_id}")
661
+
662
+ except Exception as e:
663
+ self.logger.error(f"Failed to pin app {app_id}: {e}")
664
+
665
+ def show_app_info(self, app_id: str):
666
+ """Uygulama bilgilerini göster"""
667
+ self.logger.info(f"Show app info for: {app_id}")
668
+ # TODO: Uygulama bilgi dialogu implementasyonu
669
+
670
+ def on_app_launched(self, event):
671
+ """Uygulama başlatıldı olayı"""
672
+ try:
673
+ app_id = event.get("app_id")
674
+ if app_id and app_id not in self.running_apps:
675
+ self.running_apps.append(app_id)
676
+ self.update_dock_running_states()
677
+ except Exception as e:
678
+ self.logger.error(f"Failed to handle app launched event: {e}")
679
+
680
+ def on_app_closed(self, event):
681
+ """Uygulama kapatıldı olayı"""
682
+ try:
683
+ app_id = event.get("app_id")
684
+ if app_id and app_id in self.running_apps:
685
+ self.running_apps.remove(app_id)
686
+ self.update_dock_running_states()
687
+ except Exception as e:
688
+ self.logger.error(f"Failed to handle app closed event: {e}")
689
+
690
+ def get_dock_config(self) -> Dict:
691
+ """Dock konfigürasyonunu al"""
692
+ return {
693
+ "pinned_apps": self.pinned_apps,
694
+ "dock_height": self.dock_height,
695
+ "dock_margin": self.dock_margin,
696
+ "icon_spacing": self.icon_spacing
697
+ }
698
+
699
+ def apply_dock_config(self, config: Dict):
700
+ """Dock konfigürasyonunu uygula"""
701
+ try:
702
+ if "pinned_apps" in config:
703
+ self.pinned_apps = config["pinned_apps"]
704
+ self.update_dock_icons()
705
+
706
+ if "dock_height" in config:
707
+ self.dock_height = config["dock_height"]
708
+ self.setFixedHeight(self.dock_height + self.dock_margin)
709
+
710
+ if "dock_margin" in config:
711
+ self.dock_margin = config["dock_margin"]
712
+
713
+ if "icon_spacing" in config:
714
+ self.icon_spacing = config["icon_spacing"]
715
+ self.dock_layout.setSpacing(self.icon_spacing)
716
+
717
+ except Exception as e:
718
+ self.logger.error(f"Failed to apply dock config: {e}")
719
+
720
+ # Geriye uyumluluk için alias
721
+ ModernDock = ModernRainDock
722
+ RainDock = ModernRainDock # Eski isim için de alias