feat: add calculator app
This commit is contained in:
parent
1664b40480
commit
5734a26cb1
|
@ -87,6 +87,14 @@ useResizable(windowRef, {
|
||||||
onResizeStart: () => { isResizing.value = true; },
|
onResizeStart: () => { isResizing.value = true; },
|
||||||
onResizeEnd: () => { isResizing.value = false; },
|
onResizeEnd: () => { isResizing.value = false; },
|
||||||
constraints: draggableConstraints,
|
constraints: draggableConstraints,
|
||||||
|
maxSize: {
|
||||||
|
width: props.instance.maxWidth,
|
||||||
|
height: props.instance.maxHeight
|
||||||
|
},
|
||||||
|
minSize: {
|
||||||
|
width: props.instance.minWidth,
|
||||||
|
height: props.instance.minHeight
|
||||||
|
},
|
||||||
enabled: isDraggableAndResizable,
|
enabled: isDraggableAndResizable,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -135,8 +135,9 @@ const formattedDisplay = computed(() => {
|
||||||
background: var(--window-background);
|
background: var(--window-background);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 16px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
min-height: 400px; /* Ensure minimum height for all buttons */
|
||||||
}
|
}
|
||||||
|
|
||||||
.display {
|
.display {
|
||||||
|
@ -145,16 +146,17 @@ const formattedDisplay = computed(() => {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
min-height: 60px;
|
min-height: 55px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
flex-shrink: 0; /* Prevent display from shrinking */
|
||||||
}
|
}
|
||||||
|
|
||||||
.display-value {
|
.display-value {
|
||||||
color: #00ff00;
|
color: #00ff00;
|
||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
font-size: 24px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -166,19 +168,21 @@ const formattedDisplay = computed(() => {
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
min-height: 300px; /* Ensure enough space for all button rows */
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.15s ease;
|
transition: all 0.15s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 50px;
|
min-height: 48px;
|
||||||
|
max-height: 65px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
|
|
|
@ -15,6 +15,8 @@ interface ResizeOptions {
|
||||||
initialSize: { width: number; height: number; };
|
initialSize: { width: number; height: number; };
|
||||||
initialPosition: { x: number; y: number; };
|
initialPosition: { x: number; y: number; };
|
||||||
constraints?: Ref<Constraints | undefined>;
|
constraints?: Ref<Constraints | undefined>;
|
||||||
|
maxSize?: { width?: number; height?: number; };
|
||||||
|
minSize?: { width?: number; height?: number; };
|
||||||
enabled: Ref<boolean>;
|
enabled: Ref<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ export function useResizable(
|
||||||
target: Ref<HTMLElement | null>,
|
target: Ref<HTMLElement | null>,
|
||||||
options: ResizeOptions
|
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 activeHandle: string | null = null;
|
||||||
let initialMouseX = 0;
|
let initialMouseX = 0;
|
||||||
|
@ -83,8 +85,19 @@ export function useResizable(
|
||||||
if (y + height > bottom) { height = bottom - y; }
|
if (y + height > bottom) { height = bottom - y; }
|
||||||
}
|
}
|
||||||
|
|
||||||
width = Math.max(200, width);
|
// Apply minimum size constraints
|
||||||
height = Math.max(150, height);
|
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 });
|
onResize({ x, y, width, height });
|
||||||
};
|
};
|
||||||
|
|
19
lang/en.json
19
lang/en.json
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{
|
|
||||||
"startMenu": {
|
|
||||||
"about": "關於這個專案",
|
|
||||||
"systemSettings": "系統設定...",
|
|
||||||
"toggleTheme": "切換主題",
|
|
||||||
"signOut": "登出",
|
|
||||||
"closeAllWindows": "關閉所有視窗"
|
|
||||||
},
|
|
||||||
"taskbar": {
|
|
||||||
"language": "語言",
|
|
||||||
"currentLanguage": "注"
|
|
||||||
},
|
|
||||||
"common": {
|
|
||||||
"createWindow": "建立視窗",
|
|
||||||
"close": "關閉",
|
|
||||||
"minimize": "最小化",
|
|
||||||
"maximize": "最大化"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,6 +22,10 @@ export interface AppInstance {
|
||||||
isMinimized: boolean;
|
isMinimized: boolean;
|
||||||
isMaximized: boolean;
|
isMaximized: boolean;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
|
maxWidth?: number;
|
||||||
|
maxHeight?: number;
|
||||||
|
minWidth?: number;
|
||||||
|
minHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let appInstanceIdCounter = 0;
|
let appInstanceIdCounter = 0;
|
||||||
|
@ -61,6 +65,60 @@ export const useAppsStore = defineStore('apps', () => {
|
||||||
return appInstances.value.find(instance => instance.isFocused);
|
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
|
// Actions
|
||||||
function launchApp(appId: string) {
|
function launchApp(appId: string) {
|
||||||
const app = getAppById.value(appId);
|
const app = getAppById.value(appId);
|
||||||
|
@ -69,6 +127,7 @@ export const useAppsStore = defineStore('apps', () => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const constraints = getAppSizeConstraints(appId);
|
||||||
const newInstanceId = appInstanceIdCounter++;
|
const newInstanceId = appInstanceIdCounter++;
|
||||||
const newInstance: AppInstance = {
|
const newInstance: AppInstance = {
|
||||||
id: `app-${appId}-${newInstanceId}`,
|
id: `app-${appId}-${newInstanceId}`,
|
||||||
|
@ -76,12 +135,16 @@ export const useAppsStore = defineStore('apps', () => {
|
||||||
title: `${app.name} #${newInstanceId + 1}`,
|
title: `${app.name} #${newInstanceId + 1}`,
|
||||||
x: Math.random() * 200 + 50,
|
x: Math.random() * 200 + 50,
|
||||||
y: Math.random() * 100 + 50 + 48, // Below taskbar
|
y: Math.random() * 100 + 50 + 48, // Below taskbar
|
||||||
width: 320,
|
width: constraints.width,
|
||||||
height: 400,
|
height: constraints.height,
|
||||||
zIndex: nextZIndex.value++,
|
zIndex: nextZIndex.value++,
|
||||||
isMinimized: false,
|
isMinimized: false,
|
||||||
isMaximized: false,
|
isMaximized: false,
|
||||||
isFocused: true,
|
isFocused: true,
|
||||||
|
maxWidth: constraints.maxWidth,
|
||||||
|
maxHeight: constraints.maxHeight,
|
||||||
|
minWidth: constraints.minWidth,
|
||||||
|
minHeight: constraints.minHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unfocus all other instances
|
// Unfocus all other instances
|
||||||
|
|
Loading…
Reference in New Issue