From 1664b40480f8bbb143a65dbfb556d687fc79fe09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Thu, 25 Sep 2025 13:38:59 +0800 Subject: [PATCH] feat: add calculator app --- components/AppWindow.vue | 353 +++++++++++++++++++++++++++++++++++ components/Calculator.vue | 262 ++++++++++++++++++++++++++ components/Desktop.vue | 153 +++++++++++++-- components/DesktopIcon.vue | 205 ++++++++++++++++++++ components/Taskbar.vue | 71 ++++++- components/Window.vue | 62 +++++- composables/useBreakpoint.ts | 4 +- composables/useDraggable.ts | 14 +- lang/zh-TW.json | 2 +- stores/apps.ts | 166 ++++++++++++++++ stores/desktop.ts | 105 +++++++++++ 11 files changed, 1364 insertions(+), 33 deletions(-) create mode 100644 components/AppWindow.vue create mode 100644 components/Calculator.vue create mode 100644 components/DesktopIcon.vue create mode 100644 stores/apps.ts create mode 100644 stores/desktop.ts diff --git a/components/AppWindow.vue b/components/AppWindow.vue new file mode 100644 index 0000000..1b8487d --- /dev/null +++ b/components/AppWindow.vue @@ -0,0 +1,353 @@ + + + + + diff --git a/components/Calculator.vue b/components/Calculator.vue new file mode 100644 index 0000000..40591e7 --- /dev/null +++ b/components/Calculator.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/components/Desktop.vue b/components/Desktop.vue index d25f07e..6d846a2 100644 --- a/components/Desktop.vue +++ b/components/Desktop.vue @@ -2,21 +2,31 @@ import { ref } from 'vue'; import { storeToRefs } from 'pinia'; import { useWindowsStore } from '../stores/windows'; +import { useAppsStore } from '../stores/apps'; +import { useDesktopStore } from '../stores/desktop'; import { useUIStore } from '../stores/ui'; -import { useSettingsStore } from '../stores/settings'; // Import settings store +import { useSettingsStore } from '../stores/settings'; import Window from './Window.vue'; +import AppWindow from './AppWindow.vue'; +import DesktopIcon from './DesktopIcon.vue'; import Taskbar from './Taskbar.vue'; import SnapPreview from './SnapPreview.vue'; import StartMenu from './StartMenu.vue'; import type { SnapType } from '../composables/useDraggable'; const windowsStore = useWindowsStore(); +const appsStore = useAppsStore(); +const desktopStore = useDesktopStore(); const uiStore = useUIStore(); const settingsStore = useSettingsStore(); const { orderedWindows } = storeToRefs(windowsStore); -const { isStartMenuOpen } = storeToRefs(uiStore); // Get start menu state +const { orderedAppInstances, availableApps } = storeToRefs(appsStore); +const { iconPositions } = storeToRefs(desktopStore); +const { isStartMenuOpen } = storeToRefs(uiStore); const { createWindow, snapWindow, closeAllWindows } = windowsStore; +const { launchApp, closeAllAppInstances } = appsStore; +const { initializeDesktopIcons, updateIconPosition } = desktopStore; const { closeStartMenu } = uiStore; const { toggleTheme } = settingsStore; @@ -32,11 +42,11 @@ function handleAbout() { handleMenuAction(() => console.log('About clicked')); } function handleSettings() { handleMenuAction(() => console.log('Settings clicked')); } function handleSignOut() { handleMenuAction(() => console.log('Sign Out clicked')); } function handleToggleTheme() { handleMenuAction(toggleTheme); } -function handleCloseAllWindows() { handleMenuAction(closeAllWindows); } +function handleCloseAllWindows() { handleMenuAction(() => { closeAllWindows(); closeAllAppInstances(); }); } // -------------------------------- function handleSnapPreview(snapType: SnapType) { - if (!snapType) { + if (!snapType || typeof window === 'undefined') { snapPreview.value = null; return; } @@ -60,8 +70,42 @@ function handleSnapPreview(snapType: SnapType) { function handleSnapExecute({ windowId, snapType }: { windowId: string; snapType: SnapType }) { snapPreview.value = null; - if (snapType) { - snapWindow(windowId, snapType); + if (snapType && typeof window !== 'undefined') { + // Try to snap app window first, then regular window + const appInstance = appsStore.getAppInstanceById(windowId); + if (appInstance) { + // Handle app window snapping + const taskbarHeight = 22; + const screenWidth = window.innerWidth; + const screenHeight = window.innerHeight - taskbarHeight; + + appInstance.isMaximized = false; + + switch (snapType) { + case 'left': + appInstance.x = 0; + appInstance.y = taskbarHeight; + appInstance.width = screenWidth / 2; + appInstance.height = screenHeight; + break; + case 'right': + appInstance.x = screenWidth / 2; + appInstance.y = taskbarHeight; + appInstance.width = screenWidth / 2; + appInstance.height = screenHeight; + break; + case 'top': + appInstance.x = 0; + appInstance.y = taskbarHeight; + appInstance.width = screenWidth; + appInstance.height = screenHeight; + break; + } + appsStore.focusAppInstance(windowId); + } else { + // Handle regular window snapping + snapWindow(windowId, snapType); + } } } @@ -69,10 +113,47 @@ function handleDesktopClick() { closeStartMenu(); } +// Initialize desktop icons when apps are available +function initializeDesktop() { + if (availableApps.value.length > 0) { + initializeDesktopIcons(availableApps.value); + } +} + +// Handle icon position updates +function handleIconPositionChange(appId: string, x: number, y: number) { + updateIconPosition(appId, x, y); +} + +// Handle app launch from desktop icon +function handleAppLaunch(appId: string) { + launchApp(appId); +} + +// Get app info for desktop icons +const desktopIcons = computed(() => { + return iconPositions.value.map(position => { + const app = availableApps.value.find(app => app.id === position.appId); + return app ? { ...app, ...position } : null; + }).filter(Boolean); +}); + +// Initialize desktop on mount +import { onMounted, watch } from 'vue'; +onMounted(() => { + initializeDesktop(); +}); + +// Watch for changes in available apps +watch(availableApps, () => { + initializeDesktop(); +}, { immediate: true }); +