Skip to content

Commit 794ba70

Browse files
authored
Merge pull request #3 from Tuggster/improved-windows
Better window management and desktop
2 parents fcae415 + 4720833 commit 794ba70

File tree

12 files changed

+508
-62
lines changed

12 files changed

+508
-62
lines changed

config.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
// API
12
SERVER_URL = "https://minesweeper-leaderboard.tuggi.dev";
23
// SERVER_URL = 'http://localhost:3001';
4+
LEADERBOARD_ENABLED = true;
5+
6+
// Local Storage
37
LOCAL_STORAGE_USERNAME_KEY = "ms_lb_username";
48
LOCAL_STORAGE_COLLAPSE_KEY = "ms_lb_collapsed";
59
LOCAL_STORAGE_SHORTCUTS_REVEAL_KEY = "ms_shortcuts_reveal_enabled";
@@ -8,13 +12,16 @@ LOCAL_STORAGE_COLOR_KEY = "ms_color_enabled";
812
LOCAL_STORAGE_MARKS_KEY = "ms_marks_enabled";
913
LOCAL_STORAGE_GUESS_FREE_KEY = "ms_guess_free";
1014

11-
LEADERBOARD_ENABLED = true;
15+
// Window Management
16+
WINDOW_Z_INDEX_STACK_START = 10;
1217

1318
// name
1419
// onChange ==> (state) => ...
1520
// local storage key
1621
// value
1722

23+
// State Management
24+
1825
const setPreferenceValue = (key, value) => {
1926
const pref = preferences.find((p) => p.name === key);
2027

@@ -131,3 +138,31 @@ const preferences = [
131138
document.addEventListener("DOMContentLoaded", () => {
132139
initPrefValuesFromLs();
133140
});
141+
142+
// Window Defaults
143+
144+
LEADERBOARD_ID = "leaderboard";
145+
MINESWEEPER_ID = "minesweeper";
146+
NOTEPAD_ID = "notepad";
147+
148+
const WINDOW_DEFAULTS = {
149+
[MINESWEEPER_ID]: {
150+
open: true,
151+
zIndex: WINDOW_Z_INDEX_STACK_START,
152+
},
153+
};
154+
155+
// Desktop Actions
156+
157+
const DESKTOP_CONFIG = {
158+
["desktop-open-minesweeper"]: {
159+
clickHandler: (event) => {
160+
setWindowVisibility(MINESWEEPER_ID, true);
161+
},
162+
},
163+
["desktop-open-notepad"]: {
164+
clickHandler: (event) => {
165+
setWindowVisibility(NOTEPAD_ID, true);
166+
},
167+
},
168+
};

contextmenucontroller.js

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,27 @@ window.addEventListener("load", function () {
7575
let barButtons = this.document.querySelectorAll(".options-bar *");
7676
barButtons.forEach((button) => {
7777
let buttonName = button.getAttribute("name");
78+
79+
if (!buttonName || buttonName === "") {
80+
return;
81+
}
82+
7883
let cMenu = this.document.querySelector(`.context-menu > *[name=${buttonName}]`);
84+
85+
if (!cMenu) {
86+
return;
87+
}
88+
7989
cMenu.style.visibility = "hidden";
8090

8191
let btnPos = button.getBoundingClientRect();
8292
let menuPos = cMenu.getBoundingClientRect();
8393

8494
button.addEventListener("mouseenter", function () {
95+
if (dragging) {
96+
return;
97+
}
98+
8599
btnPos = button.getBoundingClientRect();
86100
menuPos = cMenu.getBoundingClientRect();
87101

@@ -115,36 +129,4 @@ window.addEventListener("load", function () {
115129
newGame();
116130
});
117131
});
118-
119-
let draggables = document.querySelectorAll(".draggable");
120-
draggables.forEach((draggable) => {
121-
let header = draggable.children[0].children[0];
122-
header.addEventListener("mousemove", (event) => {
123-
if (event.buttons & 1) {
124-
if (!dragging) {
125-
dragging = true;
126-
startX = event.clientX;
127-
startY = event.clientY;
128-
} else {
129-
let diffX = startX - event.clientX;
130-
let diffY = startY - event.clientY;
131-
132-
let newLeft = draggable.style.left.split("px")[0] - diffX;
133-
let newTop = draggable.style.top.split("px")[0] - diffY;
134-
135-
draggable.style.left = `${newLeft}px`;
136-
draggable.style.top = `${newTop}px`;
137-
138-
startX = event.clientX;
139-
startY = event.clientY;
140-
}
141-
} else {
142-
dragging = false;
143-
}
144-
});
145-
});
146132
});
147-
148-
let dragging = false;
149-
let startX = 0;
150-
let startY = 0;

desktop.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* Desktop */
2+
3+
.desktop {
4+
display: flex;
5+
flex-direction: row;
6+
flex-wrap: wrap;
7+
8+
padding: 1.5rem;
9+
z-index: 0;
10+
11+
gap: 2rem;
12+
13+
user-select: none;
14+
}
15+
16+
/* Icons */
17+
18+
.icon {
19+
display: flex;
20+
flex-direction: column;
21+
align-items: center;
22+
23+
width: 75px;
24+
height: 75px;
25+
}
26+
27+
.icon > p {
28+
color: black;
29+
30+
background-color: #d2d2d2;
31+
padding: 2px;
32+
33+
font-family: msSansSerif;
34+
font-size: 13px;
35+
}
36+
37+
.icon.selected > p {
38+
color: white;
39+
background-color: #36b2ff;
40+
}
41+
42+
.icon > img {
43+
aspect-ratio: 1;
44+
width: 100%;
45+
46+
image-rendering: pixelated;
47+
}
48+
49+
/* Selection Box */
50+
51+
.selection-box {
52+
background-color: #0078d799;
53+
position: absolute;
54+
55+
border: 2px solid #0078d7;
56+
left: 500px;
57+
58+
z-index: 5;
59+
}

desktop.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// the selection box thing...
2+
3+
let selectDragActive = false;
4+
const startPosition = { x: 0, y: 0 };
5+
const endPosition = { x: 0, y: 0 };
6+
7+
window.addEventListener("mousemove", (event) => {
8+
if (event.buttons & 1) {
9+
// Starting a new drag
10+
if (!selectDragActive) {
11+
if (didClickWindow(event.target)) {
12+
return;
13+
}
14+
15+
selectDragActive = true;
16+
startPosition.x = event.clientX;
17+
startPosition.y = event.clientY;
18+
}
19+
20+
endPosition.x = event.clientX;
21+
endPosition.y = event.clientY;
22+
23+
doSelectionBoxLogic(getSanePositions());
24+
} else {
25+
selectDragActive = false;
26+
}
27+
displaySelectionBox();
28+
});
29+
30+
window.addEventListener("mouseup", () => {
31+
if (selectDragActive) {
32+
selectDragActive = false;
33+
doSelectionBoxLogic(getSanePositions());
34+
displaySelectionBox();
35+
}
36+
});
37+
38+
const didClickWindow = (element) => {
39+
return !!findParentOfClass(element, "draggable");
40+
};
41+
42+
const findParentOfClass = (element, classname) => {
43+
if (!element) {
44+
return null;
45+
}
46+
47+
if (!element.parentElement) {
48+
return null;
49+
}
50+
51+
const classList = element.classList;
52+
if (!classList.contains(classname)) {
53+
return findParentOfClass(element.parentElement, classname);
54+
}
55+
56+
return element;
57+
};
58+
59+
const getSanePositions = () => {
60+
// organize
61+
const { x: sX, y: sY } = startPosition;
62+
const { x: eX, y: eY } = endPosition;
63+
64+
const width = Math.abs(sX - eX);
65+
const height = Math.abs(sY - eY);
66+
67+
// Determine top left of selected box
68+
const x = sX < eX ? sX : eX;
69+
const y = sY < eY ? sY : eY;
70+
71+
return { x, y, width, height };
72+
};
73+
74+
const containedInSelection = (x, y, width, height, positions) => {
75+
const rightEdge = x + width;
76+
const bottomEdge = y + height;
77+
78+
const containerRightEdge = positions.x + positions.width;
79+
const containerBottomEdge = positions.y + positions.height;
80+
81+
const xContained = (x >= positions.x || rightEdge >= positions.x) && !(x >= containerRightEdge);
82+
83+
const yContained = (y >= positions.y || bottomEdge >= positions.y) && !(y >= containerBottomEdge);
84+
85+
return xContained && yContained;
86+
};
87+
88+
const doSelectionBoxLogic = (boxPositions) => {
89+
const desktopItems = Array.from(document.querySelectorAll(".desktop > div"));
90+
desktopItems.forEach((element) => {
91+
const bounds = element.getBoundingClientRect();
92+
const contained = containedInSelection(bounds.x, bounds.y, bounds.width, bounds.height, boxPositions);
93+
94+
if (contained) {
95+
element.classList.add("selected");
96+
} else {
97+
element.classList.remove("selected");
98+
}
99+
});
100+
};
101+
102+
const displaySelectionBox = () => {
103+
const selectionBox = document.querySelector(".selection-box");
104+
105+
if (!selectDragActive) {
106+
selectionBox.style.visibility = "hidden";
107+
return;
108+
}
109+
110+
const { x, y, width, height } = getSanePositions();
111+
112+
// apply to css
113+
selectionBox.style.visibility = "visible";
114+
selectionBox.style.width = `${width}px`;
115+
selectionBox.style.height = `${height}px`;
116+
selectionBox.style.left = `${x}px`;
117+
selectionBox.style.top = `${y}px`;
118+
// selectionBox.style.left = `${100}px`;
119+
};
120+
121+
const desktopClickHandler = (event) => {
122+
const positions = {
123+
x: event.pageX,
124+
y: event.pageY,
125+
width: 1,
126+
height: 1,
127+
};
128+
129+
doSelectionBoxLogic(positions);
130+
};
131+
132+
window.addEventListener("mousedown", desktopClickHandler);
133+
134+
// Doubleclick
135+
136+
const desktopDoubleclickHandler = (event) => {
137+
const target = event.target;
138+
139+
if (!target) {
140+
return;
141+
}
142+
143+
const parent = findParentOfClass(target, "icon");
144+
const configItem = DESKTOP_CONFIG[parent.id];
145+
146+
if (!configItem) {
147+
return;
148+
}
149+
150+
configItem?.clickHandler();
151+
};
152+
153+
window.addEventListener("load", () => {
154+
const desktopIcons = document.querySelectorAll(".desktop > .icon");
155+
156+
desktopIcons.forEach((icon) => {
157+
icon.addEventListener("dblclick", desktopDoubleclickHandler);
158+
});
159+
});

desktop/minesweeper.png

30.7 KB
Loading

desktop/notepad.png

30.7 KB
Loading

0 commit comments

Comments
 (0)