feat(tamagotchi): add "Pin on top" toggle to controls island (#1183)

\
This commit is contained in:
Liet Blue
2026-03-09 22:05:05 +08:00
committed by GitHub
parent 57b04d957b
commit 604341a1e6
8 changed files with 46 additions and 2 deletions
@@ -5,7 +5,7 @@ import { defineInvokeHandler } from '@moeru/eventa'
import { bounds, startLoopGetBounds } from '@proj-airi/electron-eventa'
import { createRendererLoop } from '@proj-airi/electron-vueuse/main'
import { electron, electronWindowClose } from '../../../shared/eventa'
import { electron, electronWindowClose, electronWindowSetAlwaysOnTop } from '../../../shared/eventa'
import { onAppBeforeQuit, onAppWindowAllClosed } from '../../libs/bootkit/lifecycle'
import { resizeWindowByDelta } from '../../windows/shared/window'
@@ -46,6 +46,17 @@ export function createWindowService(params: { context: ReturnType<typeof createC
}
})
defineInvokeHandler(params.context, electronWindowSetAlwaysOnTop, (flag, options) => {
if (params.window.webContents.id === options?.raw.ipcMainEvent.sender.id) {
if (flag) {
params.window.setAlwaysOnTop(true, 'screen-saver', 1)
}
else {
params.window.setAlwaysOnTop(false)
}
}
})
defineInvokeHandler(params.context, electron.window.setVibrancy, (vibrancy, options) => {
if (vibrancy && params.window.webContents.id === options?.raw.ipcMainEvent.sender.id) {
params.window.setVibrancy(vibrancy[0])
@@ -20,6 +20,7 @@ import {
electronOpenSettings,
electronStartDraggingWindow,
electronWindowClose,
electronWindowSetAlwaysOnTop,
} from '../../../../shared/eventa'
const { isDark, toggleDark } = useTheme()
@@ -29,11 +30,12 @@ const settingsAudioDeviceStore = useSettingsAudioDevice()
const settingsStore = useSettings()
const context = useElectronEventaContext()
const { enabled } = storeToRefs(settingsAudioDeviceStore)
const { controlsIslandIconSize } = storeToRefs(settingsStore)
const { alwaysOnTop, controlsIslandIconSize } = storeToRefs(settingsStore)
const openSettings = useElectronEventaInvoke(electronOpenSettings)
const openChat = useElectronEventaInvoke(electronOpenChat)
const isLinux = useElectronEventaInvoke(electron.app.isLinux)
const closeWindow = useElectronEventaInvoke(electronWindowClose)
const setAlwaysOnTop = useElectronEventaInvoke(electronWindowSetAlwaysOnTop)
const expanded = ref(false)
const islandRef = ref<HTMLElement>()
@@ -53,6 +55,15 @@ useIntervalFn(() => {
}
}, 1500)
// Apply alwaysOnTop on mount and when it changes
watch(alwaysOnTop, (val) => {
setAlwaysOnTop(val)
}, { immediate: true })
function toggleAlwaysOnTop() {
alwaysOnTop.value = !alwaysOnTop.value
}
// Grouped classes for icon / border / padding and combined style class
const adjustStyleClasses = computed(() => {
let isLarge: boolean
@@ -163,6 +174,16 @@ function refreshWindow() {
</template>
</ControlButtonTooltip>
<ControlButtonTooltip>
<ControlButton :button-style="adjustStyleClasses.button" @click="toggleAlwaysOnTop()">
<div v-if="alwaysOnTop" i-solar:pin-bold :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300" />
<div v-else i-solar:pin-linear :class="adjustStyleClasses.icon" text="neutral-800 dark:neutral-300 opacity-50" />
</ControlButton>
<template #tooltip>
{{ alwaysOnTop ? t('tamagotchi.stage.controls-island.unpin-from-top') : t('tamagotchi.stage.controls-island.pin-on-top') }}
</template>
</ControlButtonTooltip>
<ControlsIslandFadeOnHover :icon-class="adjustStyleClasses.icon" :button-style="adjustStyleClasses.button" />
<ControlButtonTooltip>
@@ -197,6 +197,7 @@ export const widgetsFetch = defineInvokeEventa<WidgetSnapshot | void, { id: stri
export const widgetsPrepareWindow = defineInvokeEventa<string | undefined, { id?: string }>('eventa:invoke:electron:windows:widgets:prepare')
export const electronWindowClose = defineInvokeEventa<void>('eventa:invoke:electron:window:close')
export const electronWindowSetAlwaysOnTop = defineInvokeEventa<void, boolean>('eventa:invoke:electron:window:set-always-on-top')
export const electronAppQuit = defineInvokeEventa<void>('eventa:invoke:electron:app:quit')
// Internal event from main -> widgets renderer when a widget should render
@@ -19,6 +19,8 @@ docs:
'drag-to-move-window': Drag to move window
'switch-to-light-mode': Switch to light mode
'switch-to-dark-mode': Switch to dark mode
'pin-on-top': Pin on top
'unpin-from-top': Unpin from top
'close': Close
'expand': Expand
'collapse': Collapse
@@ -19,6 +19,8 @@ docs:
'drag-to-move-window': ウィンドウをドラッグして移動
'switch-to-light-mode': ライトモードに切り替え
'switch-to-dark-mode': ダークモードに切り替え
'pin-on-top': 最前面にピン留め
'unpin-from-top': ピン留めを解除
'close': Close
'expand': Expand
'collapse': Collapse
@@ -19,6 +19,8 @@ docs:
'drag-to-move-window': 拖动以移动窗口
'switch-to-light-mode': 切换到亮色模式
'switch-to-dark-mode': 切换到暗色模式
'pin-on-top': 置顶显示
'unpin-from-top': 取消置顶
'close': 关闭
'expand': 展开
'collapse': 折叠
@@ -1,17 +1,21 @@
import { useLocalStorageManualReset } from '@proj-airi/stage-shared/composables'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useSettingsControlsIsland = defineStore('settings-controls-island', () => {
const allowVisibleOnAllWorkspaces = useLocalStorageManualReset<boolean>('settings/allow-visible-on-all-workspaces', true)
const alwaysOnTop = useLocalStorageManualReset<boolean>('settings/always-on-top', true)
const controlsIslandIconSize = useLocalStorageManualReset<'auto' | 'large' | 'small'>('settings/controls-island/icon-size', 'auto')
function resetState() {
allowVisibleOnAllWorkspaces.reset()
alwaysOnTop.value = true
controlsIslandIconSize.reset()
}
return {
allowVisibleOnAllWorkspaces,
alwaysOnTop,
controlsIslandIconSize,
resetState,
}
@@ -73,6 +73,7 @@ export const useSettings = defineStore('settings', () => {
// UI settings
allowVisibleOnAllWorkspaces: controlsIslandRefs.allowVisibleOnAllWorkspaces,
alwaysOnTop: controlsIslandRefs.alwaysOnTop,
controlsIslandIconSize: controlsIslandRefs.controlsIslandIconSize,
// Methods