Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/composables/useDevice.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { invoke } from '@tauri-apps/api/core'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { cursorPosition } from '@tauri-apps/api/window'

import { INVOKE_KEY, LISTEN_KEY } from '../constants'
import { ref } from 'vue'

import { useModel } from './useModel'
import { useTauriListen } from './useTauriListen'

import { INVOKE_KEY, LISTEN_KEY } from '@/constants'
import { useCatStore } from '@/stores/cat'
import { useModelStore } from '@/stores/model'
import { useStatisticsStore } from '@/stores/statistics'
import { inBetween } from '@/utils/is'
import { isWindows } from '@/utils/platform'

Expand All @@ -35,9 +36,11 @@ interface KeyboardEvent {
type DeviceEvent = MouseButtonEvent | MouseMoveEvent | KeyboardEvent

export function useDevice() {
const isHovering = ref(false)
const modelStore = useModelStore()
const releaseTimers = new Map<string, NodeJS.Timeout>()
const catStore = useCatStore()
const statisticsStore = useStatisticsStore()
const { handlePress, handleRelease, handleMouseChange, handleMouseMove } = useModel()

const startListening = () => {
Expand Down Expand Up @@ -68,14 +71,16 @@ export function useDevice() {

handleMouseMove(cursorPoint)

if (catStore.window.hideOnHover) {
const appWindow = getCurrentWebviewWindow()
const position = await appWindow.outerPosition()
const { width, height } = await appWindow.innerSize()
const appWindow = getCurrentWebviewWindow()
const position = await appWindow.outerPosition()
const { width, height } = await appWindow.innerSize()

const isInWindow = inBetween(cursorPoint.x, position.x, position.x + width)
&& inBetween(cursorPoint.y, position.y, position.y + height)
const isInWindow = inBetween(cursorPoint.x, position.x, position.x + width)
&& inBetween(cursorPoint.y, position.y, position.y + height)

isHovering.value = isInWindow

if (catStore.window.hideOnHover) {
document.body.style.setProperty('opacity', isInWindow ? '0' : 'unset')

if (!catStore.window.passThrough) {
Expand Down Expand Up @@ -108,6 +113,10 @@ export function useDevice() {

if (!nextValue) return

if (kind === 'KeyboardPress') {
statisticsStore.recordKeyPress(nextValue)
}

if (nextValue === 'CapsLock') {
return handleAutoRelease(nextValue)
}
Expand All @@ -127,6 +136,7 @@ export function useDevice() {

switch (kind) {
case 'MousePress':
statisticsStore.recordMouseClick(value)
return handleMouseChange(value)
case 'MouseRelease':
return handleMouseChange(value, false)
Expand All @@ -137,5 +147,6 @@ export function useDevice() {

return {
startListening,
isHovering,
}
}
16 changes: 14 additions & 2 deletions src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"opacity": "Opacity",
"autoReleaseDelay": "Auto Release Delay",
"hideOnHover": "Hide on Hover",
"position": "Window Position"
"position": "Window Position",
"statisticsSettings": "Statistics Settings",
"enableStatistics": "Enable Statistics",
"mouseClickStatistics": "Track Mouse Clicks",
"resetStatistics": "Reset Statistics"
},
"hints": {
"mirrorMode": "When enabled, the model will be mirrored horizontally.",
Expand All @@ -33,7 +37,15 @@
"windowSize": "Move mouse to window edge, or hold Shift and right-drag to resize.",
"autoReleaseDelay": "On Windows, some system keys cannot capture release events and will auto-release after timeout.",
"hideOnHover": "When enabled, the window hides when mouse hovers over it.",
"position": "Takes effect after the app starts, or when this parameter, window size, model, or screen resolution changes."
"position": "Takes effect after the app starts, or when this parameter, window size, model, or screen resolution changes.",
"enableStatistics": "When enabled, keyboard and mouse usage data will be tracked.",
"mouseClickStatistics": "When enabled, mouse clicks will be counted.",
"resetStatisticsConfirm": "Are you sure you want to reset all statistics?"
},
"buttons": {
"reset": "Reset",
"cancel": "Cancel",
"confirm": "Confirm"
},
"options": {
"topLeft": "Top Left",
Expand Down
16 changes: 14 additions & 2 deletions src/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"opacity": "Opacidade",
"autoReleaseDelay": "Atraso de Liberação Automática",
"hideOnHover": "Ocultar ao Passar o Mouse",
"position": "Posição da Janela"
"position": "Posição da Janela",
"statisticsSettings": "Configurações de Estatísticas",
"enableStatistics": "Ativar Estatísticas",
"mouseClickStatistics": "Rastrear Cliques do Mouse",
"resetStatistics": "Redefinir Estatísticas"
},
"hints": {
"mirrorMode": "Quando ativado, o modelo será invertido horizontalmente.",
Expand All @@ -33,7 +37,15 @@
"windowSize": "Mova o mouse para a borda da janela ou segure Shift e arraste com o botão direito para redimensionar.",
"autoReleaseDelay": "Devido ao Windows não capturar eventos de liberação de certas teclas de nível do sistema, elas serão automaticamente tratadas como liberadas após um tempo limite.",
"hideOnHover": "Quando ativado, a janela será ocultada quando o mouse passar sobre ela.",
"position": "Entra em vigor após inicializar o aplicativo ou quando este parâmetro, o tamanho da janela, o modelo ou a resolução de tela é alterado."
"position": "Entra em vigor após inicializar o aplicativo ou quando este parâmetro, o tamanho da janela, o modelo ou a resolução de tela é alterado.",
"enableStatistics": "Quando ativado, os dados de uso do teclado e mouse serão rastreados.",
"mouseClickStatistics": "Quando ativado, os cliques do mouse serão contados.",
"resetStatisticsConfirm": "Tem certeza de que deseja redefinir todas as estatísticas?"
},
"buttons": {
"reset": "Redefinir",
"cancel": "Cancelar",
"confirm": "Confirmar"
},
"options": {
"topLeft": "Canto Superior Esquerdo",
Expand Down
16 changes: 14 additions & 2 deletions src/locales/vi-VN.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"opacity": "Độ mờ",
"autoReleaseDelay": "Độ trễ tự động nhả phím",
"hideOnHover": "Ẩn khi di chuột",
"position": "Vị trí cửa sổ"
"position": "Vị trí cửa sổ",
"statisticsSettings": "Cài đặt Thống kê",
"enableStatistics": "Bật Thống kê",
"mouseClickStatistics": "Thống kê Click chuột",
"resetStatistics": "Đặt lại Thống kê"
},
"hints": {
"mirrorMode": "Bật để lật ngang mô hình.",
Expand All @@ -33,7 +37,15 @@
"windowSize": "Di chuyển chuột đến mép cửa sổ hoặc giữ Shift và kéo chuột phải để thay đổi kích thước.",
"autoReleaseDelay": "Do Windows không bắt được sự kiện nhả của một số phím hệ thống, các phím đó sẽ được tự động xem như đã nhả sau khi hết thời gian chờ.",
"hideOnHover": "Khi bật, cửa sổ sẽ ẩn khi chuột di chuyển vào.",
"position": "Có hiệu lực sau khi khởi động ứng dụng hoặc khi tham số này, kích thước cửa sổ, mô hình hoặc độ phân giải màn hình thay đổi."
"position": "Có hiệu lực sau khi khởi động ứng dụng hoặc khi tham số này, kích thước cửa sổ, mô hình hoặc độ phân giải màn hình thay đổi.",
"enableStatistics": "Khi bật, dữ liệu sử dụng bàn phím và chuột sẽ được thống kê.",
"mouseClickStatistics": "Khi bật, số lần click chuột sẽ được thống kê.",
"resetStatisticsConfirm": "Bạn có chắc muốn đặt lại tất cả thống kê không?"
},
"buttons": {
"reset": "Đặt lại",
"cancel": "Hủy",
"confirm": "Xác nhận"
},
"options": {
"topLeft": "Góc trên cùng bên trái",
Expand Down
16 changes: 14 additions & 2 deletions src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
"opacity": "不透明度",
"autoReleaseDelay": "按键自动释放延迟",
"hideOnHover": "鼠标移入隐藏",
"position": "窗口位置"
"position": "窗口位置",
"statisticsSettings": "统计设置",
"enableStatistics": "启用统计",
"mouseClickStatistics": "统计鼠标点击",
"resetStatistics": "重置统计"
},
"hints": {
"mirrorMode": "启用后,模型将水平镜像翻转。",
Expand All @@ -33,7 +37,15 @@
"windowSize": "将鼠标移至窗口边缘,或按住 Shift 并右键拖动,也可以调整窗口大小。",
"autoReleaseDelay": "由于 Windows 下部分系统级按键无法捕获释放事件,超时后将自动视为已释放。",
"hideOnHover": "启用后,鼠标悬停在窗口上时,窗口会隐藏。",
"position": "应用启动后,或当此参数、窗口尺寸、模型、电脑分辨率发生变化时生效。"
"position": "应用启动后,或当此参数、窗口尺寸、模型、电脑分辨率发生变化时生效。",
"enableStatistics": "启用后,将统计键盘和鼠标使用数据。",
"mouseClickStatistics": "启用后,将统计鼠标点击次数。",
"resetStatisticsConfirm": "确定要重置所有统计数据吗?"
},
"buttons": {
"reset": "重置",
"cancel": "取消",
"confirm": "确定"
},
"options": {
"topLeft": "左上角",
Expand Down
19 changes: 17 additions & 2 deletions src/pages/main/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { exists, readDir } from '@tauri-apps/plugin-fs'
import { useDebounceFn, useEventListener } from '@vueuse/core'
import { round } from 'es-toolkit'
import { nth } from 'es-toolkit/compat'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'

import { useDevice } from '@/composables/useDevice'
import { useGamepad } from '@/composables/useGamepad'
Expand All @@ -19,11 +19,13 @@ import { hideWindow, setAlwaysOnTop, setTaskbarVisibility, showWindow } from '@/
import { useCatStore } from '@/stores/cat'
import { useGeneralStore } from '@/stores/general.ts'
import { useModelStore } from '@/stores/model'
import { useStatisticsStore } from '@/stores/statistics'
import { isImage } from '@/utils/is'
import { join } from '@/utils/path'
import { clearObject } from '@/utils/shared'

const { startListening } = useDevice()
const { startListening, isHovering } = useDevice()
const statisticsStore = useStatisticsStore()
const appWindow = getCurrentWebviewWindow()
const { modelSize, handleLoad, handleDestroy, handleResize, handleKeyChange } = useModel()
const catStore = useCatStore()
Expand Down Expand Up @@ -147,6 +149,10 @@ function handleMouseMove(event: MouseEvent) {

catStore.window.scale = round(nextScale)
}

const totalCount = computed(() => {
return statisticsStore.keyboard.total + statisticsStore.mouse.total
})
</script>

<template>
Expand Down Expand Up @@ -185,5 +191,14 @@ function handleMouseMove(event: MouseEvent) {
{{ $t('pages.main.hints.redrawing') }}
</span>
</div>

<div
v-show="isHovering && statisticsStore.settings.enabled"
class="pointer-events-none flex items-end justify-center pb-[5%]"
>
<span class="rounded-full bg-black/60 px-10px py-6px text-16px text-white">
{{ totalCount }}
</span>
</div>
</div>
</template>
37 changes: 36 additions & 1 deletion src/pages/preference/components/cat/index.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
<script setup lang="ts">
import { InputNumber, Slider, Switch } from 'ant-design-vue'
import { Button, InputNumber, Popconfirm, Slider, Switch } from 'ant-design-vue'

import Position from './components/position/index.vue'

import ProList from '@/components/pro-list/index.vue'
import ProListItem from '@/components/pro-list-item/index.vue'
import { useCatStore } from '@/stores/cat'
import { useStatisticsStore } from '@/stores/statistics'
import { isWindows } from '@/utils/platform'

const catStore = useCatStore()
const statisticsStore = useStatisticsStore()

function handleReset() {
statisticsStore.reset()
}
</script>

<template>
Expand Down Expand Up @@ -105,4 +111,33 @@ const catStore = useCatStore()
/>
</ProListItem>
</ProList>

<ProList :title="$t('pages.preference.cat.labels.statisticsSettings')">
<ProListItem
:description="$t('pages.preference.cat.hints.enableStatistics')"
:title="$t('pages.preference.cat.labels.enableStatistics')"
>
<Switch v-model:checked="statisticsStore.settings.enabled" />
</ProListItem>

<ProListItem
:description="$t('pages.preference.cat.hints.mouseClickStatistics')"
:title="$t('pages.preference.cat.labels.mouseClickStatistics')"
>
<Switch v-model:checked="statisticsStore.settings.mouseClickEnabled" />
</ProListItem>

<ProListItem :title="$t('pages.preference.cat.labels.resetStatistics')">
<Popconfirm
:cancel-text="$t('pages.preference.cat.buttons.cancel')"
:ok-text="$t('pages.preference.cat.buttons.confirm')"
:title="$t('pages.preference.cat.hints.resetStatisticsConfirm')"
@confirm="handleReset"
>
<Button danger>
{{ $t('pages.preference.cat.buttons.reset') }}
</Button>
</Popconfirm>
</ProListItem>
</ProList>
</template>
77 changes: 77 additions & 0 deletions src/stores/statistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { defineStore } from 'pinia'
import { reactive } from 'vue'

export interface StatisticsStore {
keyboard: {
total: number
keys: Record<string, number>
}
mouse: {
total: number
left: number
right: number
}
settings: {
enabled: boolean
mouseClickEnabled: boolean
}
}

export const useStatisticsStore = defineStore('statistics', () => {
const keyboard = reactive<StatisticsStore['keyboard']>({
total: 0,
keys: {},
})

const mouse = reactive<StatisticsStore['mouse']>({
total: 0,
left: 0,
right: 0,
})

const settings = reactive<StatisticsStore['settings']>({
enabled: true,
mouseClickEnabled: true,
})

const recordKeyPress = (key: string) => {
if (!settings.enabled) return
keyboard.total++
keyboard.keys[key] = (keyboard.keys[key] || 0) + 1
}

const recordMouseClick = (button: string) => {
if (!settings.enabled || !settings.mouseClickEnabled) return
if (button === 'Left') {
mouse.left++
mouse.total++
} else if (button === 'Right') {
mouse.right++
mouse.total++
}
}

const reset = () => {
keyboard.total = 0
keyboard.keys = {}
mouse.total = 0
mouse.left = 0
mouse.right = 0
}

return {
keyboard,
mouse,
settings,
// actions
recordKeyPress,
recordMouseClick,
reset,
}
}, {
tauri: {
autoStart: true,
saveOnChange: true,
filterKeys: ['settings'],
},
})