Skip to content

Commit bac7891

Browse files
committed
MissionControl opener added to desktop #40
1 parent 9d8e5e3 commit bac7891

File tree

9 files changed

+268
-161
lines changed

9 files changed

+268
-161
lines changed

src/webOS/components/WelcomeWrap/messages.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Add or remove messages as needed.
22
const messages = [
3-
"Welcome",
3+
"Kia Ora",
44
"webOS is here",
55
"developed by Nikita Mogilevskii",
66
"Have fun!"

src/webOS/contexts/AppsContext/AppsContext.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import marioIcon from '../../media/icons/mario64.png';
1818
import quakeIcon from '../../media/icons/quake3.png';
1919
import Quake3 from '../../initialapps/Quake3/Quake3.jsx';
2020
import missionControlIcon from '../../media/icons/missioncontrol.png';
21+
import MissionControlOpener from '../../initialapps/MissionControlOpener/MissionControlOpener.jsx';
2122

2223
/**
2324
* The initial list of apps.
@@ -104,9 +105,9 @@ const initialAppsList = [
104105
id: 'missioncontrol',
105106
name: 'Mission Control',
106107
icon: missionControlIcon,
107-
component: null,
108+
component: MissionControlOpener,
108109
priority: 3,
109-
indock: true,
110+
indock: false,
110111
available: true,
111112
},
112113
{

src/webOS/coreservices/MissionControl/components/MissionControlUI/MissionControlUI.css

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* src/components/MissionControl/MissionControlUI.css */
1+
/* src/components/MissionControl/components/MissionControlUI/MissionControlUI.css */
22

33
/* ---------- Animations ---------- */
44

@@ -9,7 +9,7 @@
99
}
1010

1111
/* Panels flying in from center when overview-expanded */
12-
/* (This keyframe is still needed here for the initial fadein behavior.) */
12+
/* (This keyframe is still needed here for the initial fade-in behavior.) */
1313
@keyframes slideFromCenter {
1414
from {
1515
transform: translate(var(--tx), var(--ty));
@@ -41,24 +41,27 @@
4141
animation: none;
4242
}
4343

44-
/* ---------- Wallpaper ---------- */
44+
/* ---------- Wallpaper (now fixed!) ---------- */
4545
.mc-wallpaper {
46-
position: absolute;
47-
inset: 0;
46+
position: fixed; /* anchor to viewport, not the sliding container */
47+
top: 0;
48+
left: 0;
49+
width: 100vw;
50+
height: 100vh;
4851
z-index: 100;
4952
opacity: 0;
5053
animation: none;
5154
}
5255

53-
/* ---------- Overlay Toolbar (buttons at top) ---------- */
56+
/* ---------- Overlay Toolbar (buttons at top, now fixed!) ---------- */
5457
.mc-overlay {
55-
position: absolute;
58+
position: fixed; /* anchor to viewport */
5659
top: 10px;
5760
left: 50%;
5861
transform: translateX(-50%);
5962
display: flex;
6063
gap: 8px;
61-
z-index: 100;
64+
z-index: 300;
6265
}
6366
.mc-overlay button {
6467
background: rgba(0, 0, 0, 0.6);

src/webOS/coreservices/MissionControl/components/MissionControlUI/MissionControlUI.jsx

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import { useStateManager } from '../../../../stores/StateManager/StateManager.js
1515
import './MissionControlUI.css';
1616
import MissionManager from '../MissionManager/MissionManager.jsx';
1717

18-
const FADE_DURATION = 300; // CSS fade timing (ms)
19-
const SLIDE_DURATION = 300; // overview slide timing (ms)
20-
const OPEN_DELAY = 200; // delay before fade (ms)
18+
const FADE_DURATION = 300; // CSS fade timing (ms)
19+
const SLIDE_DURATION = 300; // overview slide timing (ms)
20+
const OPEN_DELAY = 200; // delay before fade (ms)
21+
// must match your CSS --desktop-slide-duration: 0.55s
22+
const DESKTOP_SLIDE_DURATION = 550; // ms
2123

2224
// Hint / touch config
2325
const EDGE_THRESHOLD = 20; // px from either edge to trigger
24-
const HOVER_DELAY = 200; // ms to hold before hint (1 s)
26+
const HOVER_DELAY = 200; // ms to hold before hint / touch‐hold
2527
const TOUCH_HINT_OFFSET = 30; // px touch‐hold peek
2628
const MOUSE_HINT_OFFSET = 10; // px hover peek
2729
const HINT_SLIDE_DURATION = 200; // ms for hover‐hint ease
@@ -31,12 +33,57 @@ const DESKTOP_SPACING = 60; // px extra between desktops
3133
const MissionControlUI = () => {
3234
// —— Context & stores ——
3335
const {
34-
createDesktop, addDesktop, switchDesktop,
35-
deleteDesktop, reorderDesktops,
36-
activeIndex, desktops
36+
createDesktop,
37+
addDesktop,
38+
switchDesktop: rawSwitchDesktop,
39+
deleteDesktop: contextDeleteDesktop,
40+
reorderDesktops,
41+
activeIndex,
42+
desktops
3743
} = useContext(MissionControlContext);
38-
const { state, addState, editStateValue } = useStateManager();
44+
const { state, editStateValue } = useStateManager();
45+
3946
const overlayVisible = state.groups.missionControl?.overlayVisible === 'true';
47+
const stateOpened = state.groups.missionControl?.isOpened === 'true';
48+
49+
// —— Initialize currentDesktopID on mount (runs exactly once) ——
50+
useEffect(() => {
51+
const desktop = desktops[activeIndex];
52+
if (desktop?.id != null) {
53+
editStateValue(
54+
'missionControl',
55+
'currentDesktopID',
56+
String(desktop.id)
57+
);
58+
}
59+
// only on mount:
60+
// eslint-disable-next-line react-hooks/exhaustive-deps
61+
}, []);
62+
63+
// —— Wrap switchDesktop so it also writes currentDesktopID when the user actually switches ——
64+
const switchDesktop = useCallback((i) => {
65+
rawSwitchDesktop(i);
66+
const desktop = desktops[i];
67+
if (desktop?.id != null) {
68+
editStateValue(
69+
'missionControl',
70+
'currentDesktopID',
71+
String(desktop.id)
72+
);
73+
}
74+
}, [rawSwitchDesktop, desktops, editStateValue]);
75+
76+
// Keep activeIndex valid after delete
77+
const handleDeleteDesktop = useCallback((index) => {
78+
contextDeleteDesktop(index);
79+
let newIndex = activeIndex;
80+
if (index === activeIndex) {
81+
newIndex = index > 0 ? index - 1 : 0;
82+
} else if (index < activeIndex) {
83+
newIndex = activeIndex - 1;
84+
}
85+
switchDesktop(newIndex);
86+
}, [contextDeleteDesktop, switchDesktop, activeIndex]);
4087

4188
// —— Wallpaper / portal readiness ——
4289
const [showWallpaperPlaceholder, setShowWallpaperPlaceholder] = useState(false);
@@ -51,14 +98,14 @@ const MissionControlUI = () => {
5198
const hintTimerRef = useRef(null);
5299
const hoverSideRef = useRef(null);
53100

54-
// —— Touch-drag state ——
101+
// —— Touchdrag state ——
55102
const [touching, setTouching] = useState(false);
56103
const [touchDelta, setTouchDelta] = useState(0);
57104
const touchStartRef = useRef({ timer: null, active: false, x0: 0, initialOffset: 0 });
105+
const initialDragRef = useRef(false);
58106

59107
// —— One-time initial-drag easing ——
60108
const [dragTransition, setDragTransition] = useState(false);
61-
const initialDragRef = useRef(false);
62109

63110
// —— Release animations ——
64111
const [releaseInProgress, setReleaseInProgress] = useState(false);
@@ -78,6 +125,12 @@ const MissionControlUI = () => {
78125
const [barExpanded, setBarExpanded] = useState(false);
79126
const [prevIndex, setPrevIndex] = useState(0);
80127

128+
// instant switch (no transition)
129+
const instantSwitchDesktop = useCallback(i => {
130+
setDisableSlideTransition(true);
131+
switchDesktop(i);
132+
}, [switchDesktop]);
133+
81134
// —— Sync portalReady on desktop changes ——
82135
useEffect(() => {
83136
const prev = prevDesktopsRef.current;
@@ -111,29 +164,9 @@ const MissionControlUI = () => {
111164
}
112165
});
113166

114-
// —— Manage 'opened' flag in stateManager ——
115-
useEffect(() => {
116-
if (!state.groups.missionControl.hasOwnProperty('opened')) {
117-
addState('missionControl', 'opened', 'false');
118-
}
119-
}, [addState, state.groups.missionControl]);
120-
121-
// —— Clear lingering 'opened' on first mount ——
122-
const initialMount = useRef(true);
123-
useEffect(() => {
124-
if (!initialMount.current) return;
125-
initialMount.current = false;
126-
if (state.groups.missionControl.opened === 'true') {
127-
editStateValue('desktop','iconVisible','true');
128-
editStateValue('desktop','menubarVisible','true');
129-
editStateValue('missionControl','opened','false');
130-
}
131-
}, []);
132-
133-
// —— Track viewport resize & disable transition on orientation change ——
167+
// —— Track viewport resize & orientation change ——
134168
useEffect(() => {
135169
const handleResize = () => {
136-
// disable the smooth slide one tick
137170
setDisableSlideTransition(true);
138171
setViewport({ width: window.innerWidth, height: window.innerHeight });
139172
};
@@ -145,7 +178,7 @@ const MissionControlUI = () => {
145178
};
146179
}, []);
147180

148-
// —— Reset transition disabling immediately after render ——
181+
// —— Reset transition disabling after one render ——
149182
useEffect(() => {
150183
if (!disableSlideTransition) return;
151184
const t = setTimeout(() => setDisableSlideTransition(false), 0);
@@ -165,12 +198,14 @@ const MissionControlUI = () => {
165198

166199
if (atRight && hoverSideRef.current !== 'right') {
167200
clearTimeout(hintTimerRef.current);
168-
setShowLeftHint(false); setShowRightHint(false);
201+
setShowLeftHint(false);
202+
setShowRightHint(false);
169203
hoverSideRef.current = 'right';
170204
hintTimerRef.current = setTimeout(() => setShowRightHint(true), HOVER_DELAY);
171205
} else if (atLeft && hoverSideRef.current !== 'left') {
172206
clearTimeout(hintTimerRef.current);
173-
setShowRightHint(false); setShowLeftHint(false);
207+
setShowRightHint(false);
208+
setShowLeftHint(false);
174209
hoverSideRef.current = 'left';
175210
hintTimerRef.current = setTimeout(() => setShowLeftHint(true), HOVER_DELAY);
176211
} else if (!atRight && !atLeft && hoverSideRef.current) {
@@ -206,7 +241,7 @@ const MissionControlUI = () => {
206241
return () => window.removeEventListener('click', onClick);
207242
}, [showRightHint, showLeftHint, viewport.width, activeIndex, switchDesktop]);
208243

209-
// —— Touch‐hold + draggable swipe ——
244+
// —— Touch‐hold + draggable swipe (copied verbatim) ——
210245
useEffect(() => {
211246
if (overviewOpen) return;
212247
const canRight = desktops.length > activeIndex + 1;
@@ -257,7 +292,7 @@ const MissionControlUI = () => {
257292
if (hoverSideRef.current === 'right') {
258293
newDelta = Math.max(newDelta, -slideAmount);
259294
} else {
260-
newDelta = Math.min(newDelta, slideAmount);
295+
newDelta = Math.min(newDelta, slideAmount);
261296
}
262297

263298
setTouchDelta(newDelta);
@@ -285,7 +320,7 @@ const MissionControlUI = () => {
285320
const threshold = viewport.width / 6;
286321
const willSwitch =
287322
(hoverSideRef.current === 'right' && finalDx < -threshold) ||
288-
(hoverSideRef.current === 'left' && finalDx > threshold);
323+
(hoverSideRef.current === 'left' && finalDx > threshold);
289324

290325
if (willSwitch) {
291326
const nextIdx = activeIndex + (finalDx < 0 ? 1 : -1);
@@ -352,42 +387,56 @@ const MissionControlUI = () => {
352387
const t = setTimeout(() => {
353388
editStateValue('desktop','iconVisible','false');
354389
editStateValue('desktop','menubarVisible','false');
355-
editStateValue('missionControl','opened','true');
356390
setIsFading(true);
357391
}, OPEN_DELAY);
358392
return () => clearTimeout(t);
359393
}, [activeIndex, editStateValue]);
360394

361-
const instantSwitchDesktop = useCallback(i => {
362-
setDisableSlideTransition(true);
363-
switchDesktop(i);
364-
}, [switchDesktop]);
365-
366395
const exitOverview = useCallback((restore = true) => {
367-
setShowWallpaperPlaceholder(false);
368396
setOverviewOpen(false);
369397
setIsFading(false);
370398
setBarExpanded(false);
371-
if (state.groups.missionControl.opened === 'true') {
372-
editStateValue('desktop','iconVisible','true');
373-
editStateValue('desktop','menubarVisible','true');
374-
editStateValue('missionControl','opened','false');
399+
editStateValue('desktop','iconVisible','true');
400+
editStateValue('desktop','menubarVisible','true');
401+
editStateValue('missionControl','isOpened','false');
402+
403+
if (restore) {
404+
setDisableSlideTransition(false);
405+
switchDesktop(prevIndex);
406+
setTimeout(() => {
407+
setShowWallpaperPlaceholder(false);
408+
}, DESKTOP_SLIDE_DURATION);
409+
} else {
410+
setShowWallpaperPlaceholder(false);
375411
}
376-
if (restore) instantSwitchDesktop(prevIndex);
377-
}, [prevIndex, editStateValue, state.groups.missionControl.opened, instantSwitchDesktop]);
412+
}, [prevIndex, editStateValue, switchDesktop]);
413+
414+
// —— Click handler for the UI button ——
415+
const handleOpenClick = useCallback(() => {
416+
editStateValue('missionControl','isOpened','true');
417+
openOverview();
418+
}, [editStateValue, openOverview]);
419+
420+
// —— External open/close triggers ——
421+
useEffect(() => {
422+
if (stateOpened && !overviewOpen) openOverview();
423+
}, [stateOpened, overviewOpen, openOverview]);
424+
useEffect(() => {
425+
if (!stateOpened && overviewOpen) exitOverview();
426+
}, [stateOpened, overviewOpen, exitOverview]);
378427

379-
// —— Drag-drop reorder ——
380-
const onDragStart = useCallback((e,i) => {
428+
// —— Dragdrop reorder ——
429+
const onDragStart = useCallback((e, i) => {
381430
e.dataTransfer.setData('text/plain', String(i));
382431
}, []);
383-
const onDragOver = useCallback(e => {
432+
const onDragOver = useCallback(e => {
384433
e.preventDefault();
385434
e.dataTransfer.dropEffect = 'move';
386435
}, []);
387-
const onDrop = useCallback((e,to) => {
436+
const onDrop = useCallback((e, to) => {
388437
e.preventDefault();
389-
const from = parseInt(e.dataTransfer.getData('text/plain'),10);
390-
if (!isNaN(from) && from!==to) reorderDesktops(from,to);
438+
const from = parseInt(e.dataTransfer.getData('text/plain'), 10);
439+
if (!isNaN(from) && from !== to) reorderDesktops(from, to);
391440
}, [reorderDesktops]);
392441

393442
// —— Compute wrapper transform & style ——
@@ -457,10 +506,10 @@ const MissionControlUI = () => {
457506
disabled={desktops.length === 1}
458507
>Next ›</button>
459508
<button
460-
onClick={() => deleteDesktop(activeIndex)}
509+
onClick={() => handleDeleteDesktop(activeIndex)}
461510
disabled={desktops.length === 1}
462511
>🗑 Delete</button>
463-
<button onClick={openOverview}>Mission Control</button>
512+
<button onClick={handleOpenClick}>Mission Control</button>
464513
</div>
465514
)}
466515

@@ -481,7 +530,7 @@ const MissionControlUI = () => {
481530
onDrop={onDrop}
482531
viewport={viewport}
483532
createDesktop={addDesktop}
484-
deleteDesktop={deleteDesktop}
533+
deleteDesktop={handleDeleteDesktop}
485534
/>
486535
</div>
487536
);

0 commit comments

Comments
 (0)