perf: 🚀 Extended floating control bar function

This commit is contained in:
viarotel 2024-09-13 19:38:29 +08:00
parent 6991d1fee2
commit 790e70349a
15 changed files with 313 additions and 107 deletions

View File

@ -25,7 +25,7 @@
type="primary"
text
class="!px-2 !h-full"
icon="Switch"
icon="ArrowDown"
@click="switchDevice"
>
<span class="mr-2">{{ deviceInfo.$remark || deviceInfo.$name }}</span>
@ -107,7 +107,7 @@ async function switchDevice(e) {
const data = await deviceStore.getList()
window.electron.ipcRenderer.send('show-device-list', data)
window.electron.ipcRenderer.invoke('open-control-device-menu', data)
}
</script>

View File

@ -0,0 +1,24 @@
import { BrowserWindow, ipcMain, Menu } from 'electron'
import { openControlWindow } from '$control/electron/helpers/index.js'
export default function (controlWindow) {
ipcMain.handle('open-control-device-menu', (event, deviceList) => {
const template = deviceList.map((item) => {
let label = item.$remark || item.$name
if (item.$wifi) {
label += ` (WIFI)`
}
return {
label,
click: () => {
openControlWindow(controlWindow, item)
},
}
})
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
})
}

View File

@ -0,0 +1,21 @@
import { BrowserWindow, ipcMain, Menu } from 'electron'
export default function (controlWindow) {
ipcMain.handle('open-device-gnirehtet-menu', openDeviceGnirehtetMenu)
function openDeviceGnirehtetMenu(event, args = {}) {
const { options = [] } = args
const template = options.map((item) => {
return {
label: item.label,
click() {
controlWindow.webContents.send(item.value)
},
}
})
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
}
}

View File

@ -0,0 +1,4 @@
export { default as devices } from './devices/index.js'
export { default as gnirehtet } from './gnirehtet/index.js'
export { default as rotation } from './rotation/index.js'
export { default as volume } from './volume/index.js'

View File

@ -0,0 +1,24 @@
import { BrowserWindow, ipcMain, Menu } from 'electron'
export default function (controlWindow) {
ipcMain.handle('open-device-rotation-menu', openDeviceRotationMenu)
function openDeviceRotationMenu(event, args = {}) {
const { options = [] } = args
const template = options.map((item) => {
return {
label: item.label,
click: () => {
controlWindow.webContents.send(
'execute-device-rotation-shell',
item.value,
)
},
}
})
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
}
}

View File

@ -0,0 +1,21 @@
import { BrowserWindow, ipcMain, Menu } from 'electron'
export default function (controlWindow) {
ipcMain.handle('open-device-volume-menu', openDeviceVolumeMenu)
function openDeviceVolumeMenu(event, args = {}) {
const { options = [] } = args
const template = options.map((item) => {
return {
label: item.label,
click() {
controlWindow.webContents.send('execute-device-volume-shell', item.value)
},
}
})
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
}
}

View File

@ -1,56 +1,41 @@
import { BrowserWindow, ipcMain, Menu } from 'electron'
import { BrowserWindow, ipcMain } from 'electron'
import { initControlWindow, openControlWindow } from './helpers/index.js'
import { devices, gnirehtet, rotation, volume } from './events/index.js'
function onControlMounted(controlWindow) {
ipcMain.on('language-change', (event, data) => {
controlWindow.webContents.send('language-change', data)
})
ipcMain.on('theme-change', (event, data) => {
controlWindow.webContents.send('theme-change', data)
})
rotation(controlWindow)
devices(controlWindow)
volume(controlWindow)
gnirehtet(controlWindow)
}
export default (mainWindow) => {
let controlWindow
ipcMain.on('open-control-window', (event, data) => {
ipcMain.handle('open-control-window', (event, data) => {
controlWindow = BrowserWindow.getAllWindows().find(
win => win.customId === 'control',
)
if (!controlWindow) {
controlWindow = initControlWindow(mainWindow)
ipcMain.on('control-mounted', () => {
openControlWindow(controlWindow, data)
})
if (controlWindow) {
openControlWindow(controlWindow, data)
return false
}
openControlWindow(controlWindow, data)
})
controlWindow = initControlWindow(mainWindow)
ipcMain.on('language-change', (event, data) => {
if (controlWindow) {
controlWindow.webContents.send('language-change', data)
}
})
ipcMain.on('theme-change', (event, data) => {
if (controlWindow) {
controlWindow.webContents.send('theme-change', data)
}
})
ipcMain.on('show-device-list', (event, deviceList) => {
const template = deviceList.map((item) => {
let label = item.$remark || item.$name
if (item.$wifi) {
label += ` (WIFI)`
}
return {
label,
click: () => {
openControlWindow(controlWindow, item)
},
}
ipcMain.on('control-mounted', () => {
onControlMounted(controlWindow)
openControlWindow(controlWindow, data)
})
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
})
}

View File

@ -119,14 +119,14 @@ const record = async (
)
}
const mirrorGroup = async (serial, { open = 1, ...options } = {}) => {
const mirrorGroup = async (serial, { openNum = 1, ...options } = {}) => {
const overlayDisplay
= appStore.get(`scrcpy.${replaceIP(serial)}.--display-overlay`)
|| appStore.get('scrcpy.global.--display-overlay')
|| '1080x1920/320,secure'
const command = `settings put global overlay_display_devices "${[
...Array.from({ length: open }).keys(),
...Array.from({ length: openNum }).keys(),
]
.map(() => overlayDisplay)
.join(';')}"`

View File

@ -10,5 +10,5 @@
}
},
"exclude": ["node_modules", "dist", "dist-electron", "dist-release"],
"include": ["src", "electron"]
"include": ["src", "electron", "control"]
}

View File

@ -1,6 +1,11 @@
<template>
<el-dropdown>
<div class="" :title="device.$gnirehtetLoadingText" @click="handleStart">
<el-dropdown :disabled="floating">
<div
class=""
:title="device.$gnirehtetLoadingText"
@click="handleStart"
@mouseenter="onMouseenter"
>
<slot :loading="device.$gnirehtetLoading" />
</div>
@ -16,6 +21,7 @@
<script>
import { sleep } from '$/utils'
import { adaptiveMessage } from '$/utils/modal/index.js'
export default {
props: {
@ -23,6 +29,10 @@ export default {
type: Object,
default: () => ({}),
},
floating: {
type: Boolean,
default: false,
},
},
data() {
return {}
@ -36,10 +46,41 @@ export default {
}
},
methods: {
onMouseenter() {
if (!this.floating) {
return false
}
if (!this.device.$gnirehtetLoading) {
return false
}
window.electron.ipcRenderer.once(
'stop-device-gnirehtet',
(event, data) => {
this.handleStop()
},
)
const options = [
{
label: window.t('device.control.gnirehtet.stop'),
value: 'stop-device-gnirehtet',
},
]
window.electron.ipcRenderer.invoke('open-device-gnirehtet-menu', {
options,
})
},
preferenceData(...args) {
return this.$store.preference.getData(...args)
},
async handleStart() {
if (this.device.$gnirehtetLoading) {
return false
}
this.device.$gnirehtetLoadingText = this.$t(
'device.control.gnirehtet.running',
)
@ -48,7 +89,10 @@ export default {
try {
await this.$gnirehtet.run(this.device.id)
await sleep()
this.$message.success(this.$t('device.control.gnirehtet.start.success'))
adaptiveMessage(this.$t('device.control.gnirehtet.start.success'), {
system: this.floating,
type: 'success',
})
}
catch (error) {
this.$message.warning(error.message || 'Start service failure')
@ -64,10 +108,16 @@ export default {
try {
await this.$gnirehtet.stop(this.device.id)
await sleep()
this.$message.success(this.$t('common.success'))
adaptiveMessage(this.$t('common.success'), {
system: this.floating,
type: 'success',
})
}
catch (error) {
this.$message.warning(error.message || 'Stop service failure')
adaptiveMessage(error.message || 'Stop service failure', {
system: this.floating,
type: 'warning',
})
}
this.device.$gnirehtetLoading = false

View File

@ -1,8 +1,7 @@
<template>
<el-dropdown :disabled="loading" @command="handleCommand">
<div class="">
<slot :loading="loading" />
</div>
<el-dropdown :disabled="loading || floating" @command="handleCommand">
<slot :loading :trigger="handleTrigger" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@ -26,6 +25,10 @@ export default {
type: Object,
default: () => ({}),
},
floating: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -39,27 +42,51 @@ export default {
disable:
'content insert --uri content://settings/system --bind name:s:accelerometer_rotation --bind value:i:0',
},
options: [
}
},
computed: {
options() {
const value = [
{
label: 'device.control.rotation.vertically',
label: this.$t('device.control.rotation.vertically'),
value: 'vertically',
},
{
label: 'device.control.rotation.horizontally',
label: this.$t('device.control.rotation.horizontally'),
value: 'horizontally',
},
{
label: 'device.control.rotation.auto',
label: this.$t('device.control.rotation.auto'),
value: 'auto',
},
{
label: 'device.control.rotation.disable',
label: this.$t('device.control.rotation.disable'),
value: 'disable',
},
],
}
]
return value
},
},
methods: {
handleTrigger() {
if (!this.floating) {
return false
}
window.electron.ipcRenderer.once(
'execute-device-rotation-shell',
(event, data) => {
this.handleCommand(data)
},
)
const options = toRaw(this.options)
window.electron.ipcRenderer.invoke('open-device-rotation-menu', {
options,
})
},
async handleCommand(value) {
this.loading = true

View File

@ -1,20 +1,18 @@
<template>
<el-dropdown @command="handleCommand">
<div class="">
<slot :loading="loading" />
</div>
<el-dropdown :disabled="loading" @command="handleCommand">
<slot :loading />
<template #dropdown>
<el-dropdown-menu>
<template v-if="!loading">
<el-dropdown-item v-for="item of 4" :key="item" :command="item">
{{ $t("device.control.mirror-group.open", { num: item }) }}
<el-dropdown-item
v-for="item of options"
:key="item"
:command="item.value"
:title="item.title"
>
{{ item.label }}
</el-dropdown-item>
</template>
<el-dropdown-item v-else command="close">
<span class="" :title="$t('device.control.mirror-group.close.tips')">
{{ $t("device.control.mirror-group.close") }}
</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@ -35,6 +33,29 @@ export default {
loading: false,
}
},
computed: {
options() {
const value = []
if (this.loading) {
value.push({
label: window.t('device.control.mirror-group.close'),
value: 'close',
title: window.t('device.control.mirror-group.close.tips'),
})
}
else {
value.push(
...[1, 2, 3, 4].map(item => ({
label: this.$t('device.control.mirror-group.open', { num: item }),
value: item,
})),
)
}
return value
},
},
methods: {
scrcpyArgs(...args) {
return this.$store.preference.getScrcpyArgs(...args)
@ -55,7 +76,7 @@ export default {
try {
const res = await this.$scrcpy.mirrorGroup(this.device.id, {
open: command,
openNum: command,
title: ({ displayId }) =>
`${this.$store.device.getLabel(
this.device,

View File

@ -1,8 +1,11 @@
<template>
<el-dropdown :hide-on-click="false" @command="handleCommand">
<div class="">
<slot :loading="loading" />
</div>
<el-dropdown
:hide-on-click="false"
:disabled="loading || floating"
@command="handleCommand"
>
<slot :loading :trigger="handleTrigger" />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@ -24,6 +27,10 @@ export default {
type: Object,
default: () => ({}),
},
floating: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -33,23 +40,46 @@ export default {
'volume-up': 'input keyevent KEYCODE_VOLUME_UP',
'volume-mute': 'input keyevent KEYCODE_VOLUME_MUTE',
},
options: [
}
},
computed: {
options() {
const value = [
{
label: 'device.control.volume-up.name',
label: this.$t('device.control.volume-up.name'),
value: 'volume-up',
},
{
label: 'device.control.volume-down.name',
label: this.$t('device.control.volume-down.name'),
value: 'volume-down',
},
{
label: 'device.control.volume-mute.name',
label: this.$t('device.control.volume-mute.name'),
value: 'volume-mute',
},
],
}
]
return value
},
},
methods: {
handleTrigger() {
if (!this.floating) {
return false
}
window.electron.ipcRenderer.once(
'execute-device-volume-shell',
(event, data) => {
this.handleCommand(data)
},
)
const options = toRaw(this.options)
window.electron.ipcRenderer.invoke('open-device-volume-menu', {
options,
})
},
async handleCommand(value) {
this.loading = true

View File

@ -22,14 +22,9 @@
v-bind="{
device,
floating,
...(item.command
? {
onClick: () => handleShell(item),
}
: {}),
}"
>
<template #default="{ loading = false } = {}">
<template #default="{ loading = false, trigger } = {}">
<el-button
type="primary"
plain
@ -37,6 +32,7 @@
:disabled="device.$unauthorized"
:title="$t(item.tips || item.label)"
:loading="loading"
@click="handleClick(item, trigger)"
>
<template #icon>
<svg-icon
@ -71,7 +67,7 @@
import Application from './Application/index.vue'
import FileManage from './FileManage/index.vue'
import Gnirehtet from './Gnirehtet/index.vue'
import MirrorGroup from './MirrorGroup/index.vue'
import Synergy from './Synergy/index.vue'
import Rotation from './Rotation/index.vue'
import Screenshot from './Screenshot/index.vue'
import Shell from './Shell/index.vue'
@ -83,7 +79,7 @@ export default {
Screenshot,
Application,
Gnirehtet,
MirrorGroup,
Synergy,
Rotation,
Volume,
FileManage,
@ -110,33 +106,28 @@ export default {
label: 'device.control.switch',
elIcon: 'Switch',
command: 'input keyevent 187',
visibleList: ['floating'],
},
{
label: 'device.control.home',
svgIcon: 'home',
command: 'input keyevent 3',
visibleList: ['floating'],
},
{
label: 'device.control.return',
elIcon: 'Back',
command: 'input keyevent 4',
visibleList: ['floating'],
},
{
label: 'device.control.notification',
elIcon: 'Notification',
command: 'cmd statusbar expand-notifications',
tips: 'device.control.notification.tips',
visibleList: ['floating'],
},
{
label: 'device.control.power',
elIcon: 'SwitchButton',
command: 'input keyevent 26',
tips: 'device.control.power.tips',
visibleList: ['floating'],
},
{
label: 'device.control.rotation.name',
@ -152,35 +143,35 @@ export default {
label: 'device.control.capture',
elIcon: 'Crop',
component: 'Screenshot',
visibleList: ['floating'],
},
{
label: 'device.control.reboot',
elIcon: 'RefreshLeft',
command: 'reboot',
visibleList: ['floating'],
},
{
label: 'device.control.install',
svgIcon: 'install',
component: 'Application',
visibleList: ['floating'],
},
{
label: 'device.control.file.name',
svgIcon: 'file-send',
component: 'FileManage',
hiddenKeys: ['floating'],
},
{
label: 'device.control.shell.name',
svgIcon: 'command',
component: 'Shell',
tips: 'device.control.shell.tips',
hiddenKeys: ['floating'],
},
{
label: 'device.task.name',
elIcon: 'Clock',
component: 'Tasks',
hiddenKeys: ['floating'],
},
{
label: 'device.control.gnirehtet',
@ -192,15 +183,16 @@ export default {
label: 'device.control.mirror-group.name',
svgIcon: 'multi-screen',
iconClass: '',
component: 'MirrorGroup',
component: 'Synergy',
tips: 'device.control.mirror-group.tips',
hiddenKeys: ['floating'],
},
]
return value.filter(
item =>
!this.floating || (item.visibleList ?? []).includes('floating'),
)
const handler = item =>
!(item.hiddenKeys || []).some(key => this.$props[key])
return value.filter(item => handler(item))
},
},
methods: {
@ -210,8 +202,15 @@ export default {
handleNext() {
this.$refs.scrollableRef.scrollForward()
},
handleShell(row) {
this.$adb.deviceShell(this.device.id, row.command)
handleClick(row, trigger) {
if (row?.command) {
this.$adb.deviceShell(this.device.id, row.command)
return false
}
if (trigger) {
trigger(row)
}
},
},
}

View File

@ -80,5 +80,5 @@ export function openFloatControl(deviceInfo) {
return false
}
window.electron.ipcRenderer.send('open-control-window', deviceInfo)
window.electron.ipcRenderer.invoke('open-control-window', deviceInfo)
}