feat: add calculator app

This commit is contained in:
王性驊 2025-09-25 14:38:14 +08:00
parent 1664b40480
commit 5734a26cb1
6 changed files with 98 additions and 48 deletions

View File

@ -87,6 +87,14 @@ useResizable(windowRef, {
onResizeStart: () => { isResizing.value = true; },
onResizeEnd: () => { isResizing.value = false; },
constraints: draggableConstraints,
maxSize: {
width: props.instance.maxWidth,
height: props.instance.maxHeight
},
minSize: {
width: props.instance.minWidth,
height: props.instance.minHeight
},
enabled: isDraggableAndResizable,
});

View File

@ -135,8 +135,9 @@ const formattedDisplay = computed(() => {
background: var(--window-background);
display: flex;
flex-direction: column;
padding: 16px;
padding: 20px;
box-sizing: border-box;
min-height: 400px; /* Ensure minimum height for all buttons */
}
.display {
@ -145,16 +146,17 @@ const formattedDisplay = computed(() => {
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
min-height: 60px;
min-height: 55px;
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0; /* Prevent display from shrinking */
}
.display-value {
color: #00ff00;
font-family: 'Courier New', monospace;
font-size: 24px;
font-size: 20px;
font-weight: bold;
text-align: right;
word-break: break-all;
@ -166,19 +168,21 @@ const formattedDisplay = computed(() => {
grid-template-columns: repeat(4, 1fr);
gap: 8px;
flex: 1;
min-height: 300px; /* Ensure enough space for all button rows */
}
.btn {
border: none;
border-radius: 8px;
font-size: 18px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
min-height: 50px;
min-height: 48px;
max-height: 65px;
}
.btn:hover {

View File

@ -15,6 +15,8 @@ interface ResizeOptions {
initialSize: { width: number; height: number; };
initialPosition: { x: number; y: number; };
constraints?: Ref<Constraints | undefined>;
maxSize?: { width?: number; height?: number; };
minSize?: { width?: number; height?: number; };
enabled: Ref<boolean>;
}
@ -22,7 +24,7 @@ export function useResizable(
target: Ref<HTMLElement | null>,
options: ResizeOptions
) {
const { onResize, onResizeStart, onResizeEnd, initialSize, initialPosition, constraints, enabled } = options;
const { onResize, onResizeStart, onResizeEnd, initialSize, initialPosition, constraints, maxSize, minSize, enabled } = options;
let activeHandle: string | null = null;
let initialMouseX = 0;
@ -83,8 +85,19 @@ export function useResizable(
if (y + height > bottom) { height = bottom - y; }
}
width = Math.max(200, width);
height = Math.max(150, height);
// Apply minimum size constraints
const minWidth = minSize?.width || 200;
const minHeight = minSize?.height || 150;
width = Math.max(minWidth, width);
height = Math.max(minHeight, height);
// Apply maximum size constraints
if (maxSize?.width) {
width = Math.min(maxSize.width, width);
}
if (maxSize?.height) {
height = Math.min(maxSize.height, height);
}
onResize({ x, y, width, height });
};

View File

@ -1,19 +0,0 @@
{
"startMenu": {
"about": "About This Project",
"systemSettings": "System Settings...",
"toggleTheme": "Toggle Theme",
"signOut": "Sign Out",
"closeAllWindows": "Close All Windows"
},
"taskbar": {
"language": "Language",
"currentLanguage": "EN"
},
"common": {
"createWindow": "Create Window",
"close": "Close",
"minimize": "Minimize",
"maximize": "Maximize"
}
}

View File

@ -1,19 +0,0 @@
{
"startMenu": {
"about": "關於這個專案",
"systemSettings": "系統設定...",
"toggleTheme": "切換主題",
"signOut": "登出",
"closeAllWindows": "關閉所有視窗"
},
"taskbar": {
"language": "語言",
"currentLanguage": "注"
},
"common": {
"createWindow": "建立視窗",
"close": "關閉",
"minimize": "最小化",
"maximize": "最大化"
}
}

View File

@ -22,6 +22,10 @@ export interface AppInstance {
isMinimized: boolean;
isMaximized: boolean;
isFocused: boolean;
maxWidth?: number;
maxHeight?: number;
minWidth?: number;
minHeight?: number;
}
let appInstanceIdCounter = 0;
@ -61,6 +65,60 @@ export const useAppsStore = defineStore('apps', () => {
return appInstances.value.find(instance => instance.isFocused);
});
// Get default size and constraints for specific app
function getAppSizeConstraints(appId: string) {
const appConstraints: Record<string, {
width: number;
height: number;
maxWidth?: number;
maxHeight?: number;
minWidth?: number;
minHeight?: number;
}> = {
'calculator': {
width: 300,
height: 450,
maxWidth: 350, // Prevent calculator from getting too wide
maxHeight: 500, // Prevent calculator from getting too tall
minWidth: 250, // Minimum usable width
minHeight: 400 // Minimum usable height
},
'text-editor': {
width: 600,
height: 400,
maxWidth: 1200,
maxHeight: 800,
minWidth: 400,
minHeight: 300
},
'file-manager': {
width: 800,
height: 500,
maxWidth: 1400,
maxHeight: 900,
minWidth: 600,
minHeight: 400
},
'web-browser': {
width: 1000,
height: 600,
maxWidth: 1600,
maxHeight: 1000,
minWidth: 800,
minHeight: 500
},
};
return appConstraints[appId] || {
width: 400,
height: 300,
maxWidth: 800,
maxHeight: 600,
minWidth: 300,
minHeight: 200
};
}
// Actions
function launchApp(appId: string) {
const app = getAppById.value(appId);
@ -69,6 +127,7 @@ export const useAppsStore = defineStore('apps', () => {
return null;
}
const constraints = getAppSizeConstraints(appId);
const newInstanceId = appInstanceIdCounter++;
const newInstance: AppInstance = {
id: `app-${appId}-${newInstanceId}`,
@ -76,12 +135,16 @@ export const useAppsStore = defineStore('apps', () => {
title: `${app.name} #${newInstanceId + 1}`,
x: Math.random() * 200 + 50,
y: Math.random() * 100 + 50 + 48, // Below taskbar
width: 320,
height: 400,
width: constraints.width,
height: constraints.height,
zIndex: nextZIndex.value++,
isMinimized: false,
isMaximized: false,
isFocused: true,
maxWidth: constraints.maxWidth,
maxHeight: constraints.maxHeight,
minWidth: constraints.minWidth,
minHeight: constraints.minHeight,
};
// Unfocus all other instances