Skip to content

Commit f6b3bad

Browse files
committed
feat(stage-tamagotchi): bring back resizing, use auto detect like auto hover
1 parent 9633270 commit f6b3bad

File tree

3 files changed

+83
-8
lines changed
  • apps/stage-tamagotchi/src/renderer

3 files changed

+83
-8
lines changed

apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { useElectronAllDisplays } from './use-electorn-all-displays'
22
export { useElectronEventaContext, useElectronEventaInvoke } from './use-electron-eventa-context'
33
export { useElectronMouse, useElectronMouseEventTarget } from './use-electron-mouse'
4+
export { useElectronMouseAroundWindowBorder } from './use-electron-mouse-around-window-border'
45
export type { UseMouseInElementReturn } from './use-electron-mouse-in-element'
56
export { useElectronMouseInElement } from './use-electron-mouse-in-element'
67
export { useElectronMouseInWindow } from './use-electron-mouse-in-window'
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { computed } from 'vue'
2+
3+
import { useElectronRelativeMouse } from '../use-electron-relative-mouse'
4+
import { useElectronWindowBounds } from '../use-electron-window-bounds'
5+
6+
export interface UseElectronMouseAroundWindowBorderOptions {
7+
/** Pixel distance from the window edge to consider as "near". */
8+
threshold?: number
9+
/** Allow a small overshoot outside the window and still count as near. Defaults to threshold. */
10+
overshoot?: number
11+
}
12+
13+
/**
14+
* Detect when the cursor is near the window border using window-relative mouse coords.
15+
* Fast path: no extra listeners; reuses existing mouse and window bounds streams.
16+
*/
17+
export function useElectronMouseAroundWindowBorder(
18+
options: UseElectronMouseAroundWindowBorderOptions = {},
19+
) {
20+
const threshold = options.threshold ?? 8
21+
const overshoot = options.overshoot ?? threshold
22+
23+
const { x, y } = useElectronRelativeMouse()
24+
const { width, height } = useElectronWindowBounds()
25+
26+
// Helpers to determine proximity to each edge. We allow a small overshoot so
27+
// users hovering slightly outside still get feedback to find the edge.
28+
const nearLeft = computed(() => Math.abs(x.value) <= threshold && y.value > -overshoot && y.value < height.value + overshoot)
29+
const nearRight = computed(() => Math.abs(x.value - width.value) <= threshold && y.value > -overshoot && y.value < height.value + overshoot)
30+
const nearTop = computed(() => Math.abs(y.value) <= threshold && x.value > -overshoot && x.value < width.value + overshoot)
31+
const nearBottom = computed(() => Math.abs(y.value - height.value) <= threshold && x.value > -overshoot && x.value < width.value + overshoot)
32+
33+
const nearTopLeft = computed(() => nearTop.value && nearLeft.value)
34+
const nearTopRight = computed(() => nearTop.value && nearRight.value)
35+
const nearBottomLeft = computed(() => nearBottom.value && nearLeft.value)
36+
const nearBottomRight = computed(() => nearBottom.value && nearRight.value)
37+
38+
const isNearAnyBorder = computed(() =>
39+
nearLeft.value
40+
|| nearRight.value
41+
|| nearTop.value
42+
|| nearBottom.value,
43+
)
44+
45+
return {
46+
x,
47+
y,
48+
width,
49+
height,
50+
nearLeft,
51+
nearRight,
52+
nearTop,
53+
nearBottom,
54+
nearTopLeft,
55+
nearTopRight,
56+
nearBottomLeft,
57+
nearBottomRight,
58+
isNearAnyBorder,
59+
}
60+
}
61+
62+
export type UseElectronMouseAroundWindowBorderReturn = ReturnType<typeof useElectronMouseAroundWindowBorder>
63+

apps/stage-tamagotchi/src/renderer/pages/index.vue

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/ind
1212
import { electron } from '../../shared/electron'
1313
import {
1414
useElectronEventaInvoke,
15+
useElectronMouseAroundWindowBorder,
1516
useElectronMouseInElement,
1617
useElectronMouseInWindow,
1718
useElectronRelativeMouse,
@@ -34,6 +35,8 @@ const { isOutside } = useElectronMouseInElement(controlsIslandRef)
3435
const isOutsideFor250Ms = debouncedRef(isOutside, 250)
3536
const { x: relativeMouseX, y: relativeMouseY } = useElectronRelativeMouse()
3637
const isTransparent = useCanvasPixelIsTransparentAtPoint(stageCanvas, relativeMouseX, relativeMouseY)
38+
const { isNearAnyBorder: isAroundWindowBorder } = useElectronMouseAroundWindowBorder({ threshold: 30 })
39+
const isAroundWindowBorderFor250Ms = debouncedRef(isAroundWindowBorder, 250)
3740
3841
const setIgnoreMouseEvents = useElectronEventaInvoke(electron.window.setIgnoreMouseEvents)
3942
@@ -46,19 +49,23 @@ const { pause, resume } = watchPausable(isTransparent, (transparent) => {
4649
shouldFadeOnCursorWithin.value = !transparent
4750
}, { immediate: true })
4851
49-
watch([isOutsideFor250Ms, isOutsideWindow], () => {
50-
if (!isOutsideFor250Ms.value) {
52+
watch([isOutsideFor250Ms, isAroundWindowBorderFor250Ms, isOutsideWindow, isTransparent], () => {
53+
const insideControls = !isOutsideFor250Ms.value
54+
const nearBorder = isAroundWindowBorderFor250Ms.value
55+
56+
if (insideControls || nearBorder) {
57+
// Inside interactive controls or near resize border: do NOT ignore events
5158
isIgnoringMouseEvents.value = false
5259
shouldFadeOnCursorWithin.value = false
5360
setIgnoreMouseEvents([false, { forward: true }])
5461
pause()
5562
}
5663
else {
64+
// Otherwise allow click-through while we fade UI based on transparency
5765
isIgnoringMouseEvents.value = true
5866
if (!isOutsideWindow.value && !isTransparent.value) {
5967
shouldFadeOnCursorWithin.value = true
6068
}
61-
6269
setIgnoreMouseEvents([true, { forward: true }])
6370
resume()
6471
}
@@ -85,6 +92,8 @@ watch([isOutsideFor250Ms, isOutsideWindow], () => {
8592
shouldFadeOnCursorWithin ? 'op-0' : 'op-100',
8693
'absolute',
8794
'top-0 left-0 w-full h-full',
95+
'overflow-hidden',
96+
'rounded-2xl',
8897
'transition-opacity duration-250 ease-in-out',
8998
]"
9099
>
@@ -161,11 +170,13 @@ watch([isOutsideFor250Ms, isOutsideWindow], () => {
161170
leave-from-class="opacity-100"
162171
leave-to-class="opacity-50"
163172
>
164-
<div
165-
v-if="false"
166-
class="absolute left-0 top-0 z-999 h-full w-full"
167-
>
168-
<div h-full w-full animate-flash animate-duration-2.5s animate-count-infinite b-4 b-primary rounded-2xl />
173+
<div v-if="isAroundWindowBorderFor250Ms && !isLoading" class="pointer-events-none absolute left-0 top-0 z-999 h-full w-full">
174+
<div
175+
:class="[
176+
'b-primary/50',
177+
'h-full w-full animate-flash animate-duration-3s animate-count-infinite b-4 rounded-2xl',
178+
]"
179+
/>
169180
</div>
170181
</Transition>
171182
</template>

0 commit comments

Comments
 (0)