diff --git a/apps/stage-tamagotchi/src/main/services/electron/window.ts b/apps/stage-tamagotchi/src/main/services/electron/window.ts index b37732c11..775fde347 100644 --- a/apps/stage-tamagotchi/src/main/services/electron/window.ts +++ b/apps/stage-tamagotchi/src/main/services/electron/window.ts @@ -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 { + 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]) diff --git a/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue b/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue index e3618985b..6e957bef2 100644 --- a/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue +++ b/apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/index.vue @@ -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() @@ -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() { + + +
+
+ + + + diff --git a/apps/stage-tamagotchi/src/shared/eventa.ts b/apps/stage-tamagotchi/src/shared/eventa.ts index 8ac4e0fa5..23f129778 100644 --- a/apps/stage-tamagotchi/src/shared/eventa.ts +++ b/apps/stage-tamagotchi/src/shared/eventa.ts @@ -197,6 +197,7 @@ export const widgetsFetch = defineInvokeEventa('eventa:invoke:electron:windows:widgets:prepare') export const electronWindowClose = defineInvokeEventa('eventa:invoke:electron:window:close') +export const electronWindowSetAlwaysOnTop = defineInvokeEventa('eventa:invoke:electron:window:set-always-on-top') export const electronAppQuit = defineInvokeEventa('eventa:invoke:electron:app:quit') // Internal event from main -> widgets renderer when a widget should render diff --git a/packages/i18n/src/locales/en/tamagotchi/stage.yaml b/packages/i18n/src/locales/en/tamagotchi/stage.yaml index ecf5008c3..9aafbd752 100644 --- a/packages/i18n/src/locales/en/tamagotchi/stage.yaml +++ b/packages/i18n/src/locales/en/tamagotchi/stage.yaml @@ -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 diff --git a/packages/i18n/src/locales/ja/tamagotchi/stage.yaml b/packages/i18n/src/locales/ja/tamagotchi/stage.yaml index 94f4c49f0..892da479b 100644 --- a/packages/i18n/src/locales/ja/tamagotchi/stage.yaml +++ b/packages/i18n/src/locales/ja/tamagotchi/stage.yaml @@ -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 diff --git a/packages/i18n/src/locales/zh-Hans/tamagotchi/stage.yaml b/packages/i18n/src/locales/zh-Hans/tamagotchi/stage.yaml index fa15cbf0c..7011df4d8 100644 --- a/packages/i18n/src/locales/zh-Hans/tamagotchi/stage.yaml +++ b/packages/i18n/src/locales/zh-Hans/tamagotchi/stage.yaml @@ -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': 折叠 diff --git a/packages/stage-ui/src/stores/settings/controls-island.ts b/packages/stage-ui/src/stores/settings/controls-island.ts index d009401ff..c57f85532 100644 --- a/packages/stage-ui/src/stores/settings/controls-island.ts +++ b/packages/stage-ui/src/stores/settings/controls-island.ts @@ -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('settings/allow-visible-on-all-workspaces', true) + const alwaysOnTop = useLocalStorageManualReset('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, } diff --git a/packages/stage-ui/src/stores/settings/index.ts b/packages/stage-ui/src/stores/settings/index.ts index 4cb062ffe..032ffd5b6 100644 --- a/packages/stage-ui/src/stores/settings/index.ts +++ b/packages/stage-ui/src/stores/settings/index.ts @@ -73,6 +73,7 @@ export const useSettings = defineStore('settings', () => { // UI settings allowVisibleOnAllWorkspaces: controlsIslandRefs.allowVisibleOnAllWorkspaces, + alwaysOnTop: controlsIslandRefs.alwaysOnTop, controlsIslandIconSize: controlsIslandRefs.controlsIslandIconSize, // Methods