windows/components/Desktop.vue

211 lines
6.2 KiB
Vue

<script setup lang="ts">
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 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 { orderedAppInstances, availableApps } = storeToRefs(appsStore);
const { iconPositions } = storeToRefs(desktopStore);
const { isStartMenuOpen } = storeToRefs(uiStore);
const { snapWindow, closeAllWindows } = windowsStore;
const { launchApp, closeAllAppInstances } = appsStore;
const { initializeDesktopIcons, updateIconPosition } = desktopStore;
const { closeStartMenu } = uiStore;
const { toggleTheme } = settingsStore;
const snapPreview = ref<{ x: number; y: number; width: number; height: number; } | null>(null);
// --- Start Menu Event Handlers ---
function handleMenuAction(action: () => void) {
action();
closeStartMenu();
}
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(); closeAllAppInstances(); }); }
// --------------------------------
function handleSnapPreview(snapType: SnapType) {
if (!snapType || typeof window === 'undefined') {
snapPreview.value = null;
return;
}
const taskbarHeight = 22;
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight - taskbarHeight;
switch (snapType) {
case 'left':
snapPreview.value = { x: 0, y: taskbarHeight, width: screenWidth / 2, height: screenHeight };
break;
case 'right':
snapPreview.value = { x: screenWidth / 2, y: taskbarHeight, width: screenWidth / 2, height: screenHeight };
break;
case 'top':
snapPreview.value = { x: 0, y: taskbarHeight, width: screenWidth, height: screenHeight };
break;
}
}
function handleSnapExecute({ windowId, snapType }: { windowId: string; snapType: SnapType }) {
snapPreview.value = null;
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);
}
}
}
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 });
</script>
<template>
<div class="desktop" @click.self="handleDesktopClick">
<!-- Regular Windows -->
<Window
v-for="window in orderedWindows"
:key="window.id"
:window="window"
@snap-preview="handleSnapPreview"
@snap-execute="handleSnapExecute"
/>
<!-- App Windows -->
<AppWindow
v-for="appInstance in orderedAppInstances"
:key="appInstance.id"
:instance="appInstance"
@snap-preview="handleSnapPreview"
@snap-execute="handleSnapExecute"
/>
<!-- Desktop Icons -->
<DesktopIcon
v-for="icon in desktopIcons"
:key="icon.appId"
:app="icon"
:x="icon.x"
:y="icon.y"
@position-change="(x, y) => handleIconPositionChange(icon.appId, x, y)"
@launch="handleAppLaunch"
/>
<SnapPreview v-if="snapPreview" :preview="snapPreview" />
<StartMenu
v-if="isStartMenuOpen"
@about="handleAbout"
@settings="handleSettings"
@toggle-theme="handleToggleTheme"
@sign-out="handleSignOut"
@close-all-windows="handleCloseAllWindows"
/>
<Taskbar />
</div>
</template>
<style scoped>
.desktop {
position: relative;
width: 100vw;
height: 100vh;
background: var(--background-desktop);
overflow: hidden;
padding-top: 22px;
}
</style>