Enhance main window controls and introduce application menu
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Updated `mainWindow.js` to include a 'quit' action in the IPC handlers, allowing the application to close gracefully. - Added a new `WindowAppMenu` component to manage application menu items, including options for 'About', 'Check For Updates', and 'Quit'. - Refactored `DashboardNavigation` to integrate the new `WindowAppMenu`, improving the user interface for non-macOS platforms. - Adjusted window control handling in `window.js` to support the new menu actions, ensuring consistent behavior across the application.
This commit is contained in:
parent
d7a7e798cb
commit
a0ea239aa6
@ -1,4 +1,4 @@
|
||||
import { BrowserWindow, ipcMain, Menu } from 'electron'
|
||||
import { app, BrowserWindow, ipcMain, Menu } from 'electron'
|
||||
import path, { dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@ -256,7 +256,7 @@ export function setupMainWindowIPC() {
|
||||
|
||||
// IPC handlers for window controls
|
||||
ipcMain.on('window-control', (event, action) => {
|
||||
if (!win) return
|
||||
if (!win && action !== 'quit') return
|
||||
switch (action) {
|
||||
case 'minimize':
|
||||
win.minimize()
|
||||
@ -274,6 +274,14 @@ export function setupMainWindowIPC() {
|
||||
case 'fullscreen':
|
||||
win.setFullScreen(!win.isFullScreen())
|
||||
break
|
||||
case 'quit':
|
||||
app.quit()
|
||||
break
|
||||
case 'toggle-devtools':
|
||||
if (win && !win.isDestroyed()) {
|
||||
win.webContents.toggleDevTools()
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ import SettingsIcon from '../../Icons/SettingsIcon'
|
||||
import DeveloperIcon from '../../Icons/DeveloperIcon'
|
||||
import { ElectronContext } from '../context/ElectronContext'
|
||||
import DashboardWindowButtons from './DashboardWindowButtons'
|
||||
import WindowAppMenu from './WindowAppMenu'
|
||||
import WebAppSwitcher from './WebAppSwitcher'
|
||||
import {
|
||||
getSidebarDefaultPath,
|
||||
@ -141,7 +142,6 @@ const DashboardNavigation = () => {
|
||||
setSidebarViewMenu(sections)
|
||||
}, [isElectron, setSidebarViewMenu])
|
||||
|
||||
const showAppLogo = isElectron && platform != 'darwin'
|
||||
const isMacOSApp = isElectron && platform == 'darwin'
|
||||
const isOtherApp = isElectron && platform != 'darwin'
|
||||
|
||||
@ -153,22 +153,15 @@ const DashboardNavigation = () => {
|
||||
const navigationContents = (
|
||||
<Flex style={{ width: '100%' }} align='center'>
|
||||
{isMacOSApp ? <DashboardWindowButtons /> : null}
|
||||
{showAppLogo == true && (
|
||||
{isOtherApp ? (
|
||||
<>
|
||||
<FarmControlLogoSmall
|
||||
style={{
|
||||
fontSize: '46px',
|
||||
height: '16px',
|
||||
marginLeft: '13px',
|
||||
marginRight: '18px'
|
||||
}}
|
||||
/>
|
||||
<WindowAppMenu />{' '}
|
||||
<Divider
|
||||
type='vertical'
|
||||
style={{ height: '14px', margin: '3px 3px 0 0' }}
|
||||
style={{ height: '14px', margin: '3px 3px 0 1.5px' }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
{showDesktopLogo == true ? (
|
||||
<FarmControlLogo
|
||||
style={{
|
||||
|
||||
256
src/components/Dashboard/common/WindowAppMenu.jsx
Normal file
256
src/components/Dashboard/common/WindowAppMenu.jsx
Normal file
@ -0,0 +1,256 @@
|
||||
import { useContext, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Button, Dropdown, Flex } from 'antd'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { ElectronContext } from '../context/ElectronContext'
|
||||
import { useAppUpdateContext } from '../context/AppUpdateContext'
|
||||
import { getSidebarMenuSections } from '../../../database/Sidebars'
|
||||
import FarmControlLogoSmall from '../../Logos/FarmControlLogoSmall'
|
||||
|
||||
const runEditCommand = (command) => {
|
||||
try {
|
||||
document.execCommand(command)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[WindowAppMenu] Failed to run edit command: ${command}`,
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapSidebarItemsToMenuItems = (items = [], navigate) =>
|
||||
items
|
||||
.map((item, index) => {
|
||||
if (item?.type === 'divider') {
|
||||
return { type: 'divider', key: `divider-${index}` }
|
||||
}
|
||||
|
||||
if (item?.children?.length) {
|
||||
return {
|
||||
key: item.key || `group-${item.label}-${index}`,
|
||||
label: item.label,
|
||||
children: mapSidebarItemsToMenuItems(item.children, navigate)
|
||||
}
|
||||
}
|
||||
|
||||
if (item?.path) {
|
||||
return {
|
||||
key: item.path,
|
||||
label: item.label,
|
||||
onClick: () => navigate(item.path)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: item.key || `disabled-${item.label}-${index}`,
|
||||
label: item.label,
|
||||
disabled: true
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
const MenuButton = ({ label, items }) => (
|
||||
<Dropdown menu={{ items }} trigger={['click']} placement='bottomLeft'>
|
||||
<Button
|
||||
type='text'
|
||||
size='small'
|
||||
className='electrobun-webkit-app-region-no-drag'
|
||||
style={{
|
||||
height: '28px',
|
||||
paddingInline: '8px',
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
</Dropdown>
|
||||
)
|
||||
|
||||
MenuButton.propTypes = {
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,
|
||||
items: PropTypes.array.isRequired
|
||||
}
|
||||
|
||||
const WindowAppMenu = () => {
|
||||
const navigate = useNavigate()
|
||||
const { handleWindowControl } = useContext(ElectronContext)
|
||||
const { checkForUpdates } = useAppUpdateContext()
|
||||
const includeDev = import.meta.env.DEV
|
||||
|
||||
const viewSections = useMemo(
|
||||
() => getSidebarMenuSections({ includeDev }),
|
||||
[includeDev]
|
||||
)
|
||||
|
||||
const appMenuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'about',
|
||||
label: 'About Farm Control',
|
||||
onClick: () => navigate('/dashboard/management/about')
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'check-for-updates',
|
||||
label: 'Check For Updates...',
|
||||
onClick: () => checkForUpdates()
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'quit',
|
||||
label: 'Quit Farm Control',
|
||||
onClick: () => handleWindowControl('quit')
|
||||
}
|
||||
],
|
||||
[checkForUpdates, handleWindowControl, navigate]
|
||||
)
|
||||
|
||||
const fileMenuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'close',
|
||||
label: 'Close Window',
|
||||
onClick: () => handleWindowControl('close')
|
||||
}
|
||||
],
|
||||
[handleWindowControl]
|
||||
)
|
||||
|
||||
const editMenuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'undo',
|
||||
label: 'Undo',
|
||||
onClick: () => runEditCommand('undo')
|
||||
},
|
||||
{
|
||||
key: 'redo',
|
||||
label: 'Redo',
|
||||
onClick: () => runEditCommand('redo')
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'cut',
|
||||
label: 'Cut',
|
||||
onClick: () => runEditCommand('cut')
|
||||
},
|
||||
{
|
||||
key: 'copy',
|
||||
label: 'Copy',
|
||||
onClick: () => runEditCommand('copy')
|
||||
},
|
||||
{
|
||||
key: 'paste',
|
||||
label: 'Paste',
|
||||
onClick: () => runEditCommand('paste')
|
||||
},
|
||||
{
|
||||
key: 'pasteAndMatchStyle',
|
||||
label: 'Paste and Match Style',
|
||||
onClick: () => runEditCommand('paste')
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'Delete',
|
||||
onClick: () => runEditCommand('delete')
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'selectAll',
|
||||
label: 'Select All',
|
||||
onClick: () => runEditCommand('selectAll')
|
||||
}
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const viewMenuItems = useMemo(() => {
|
||||
const sectionItems =
|
||||
viewSections.length > 0
|
||||
? viewSections.map((section) => ({
|
||||
key: `view-section-${section.key}`,
|
||||
label: section.label,
|
||||
children: mapSidebarItemsToMenuItems(section.items || [], navigate)
|
||||
}))
|
||||
: [
|
||||
{
|
||||
key: 'no-sidebar-items',
|
||||
label: 'No sidebar items available',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
|
||||
if (!includeDev) {
|
||||
return sectionItems
|
||||
}
|
||||
|
||||
return [
|
||||
...sectionItems,
|
||||
{ type: 'divider' },
|
||||
{
|
||||
key: 'toggle-devtools',
|
||||
label: 'Toggle Developer Tools',
|
||||
onClick: () => handleWindowControl('toggle-devtools')
|
||||
}
|
||||
]
|
||||
}, [handleWindowControl, includeDev, navigate, viewSections])
|
||||
|
||||
const windowMenuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'minimize',
|
||||
label: 'Minimize',
|
||||
onClick: () => handleWindowControl('minimize')
|
||||
},
|
||||
{
|
||||
key: 'zoom',
|
||||
label: 'Zoom',
|
||||
onClick: () => handleWindowControl('maximize')
|
||||
}
|
||||
],
|
||||
[handleWindowControl]
|
||||
)
|
||||
|
||||
const menus = useMemo(
|
||||
() => [
|
||||
{ key: 'app', label: 'Farm Control', items: appMenuItems },
|
||||
{ key: 'file', label: 'File', items: fileMenuItems },
|
||||
{ key: 'edit', label: 'Edit', items: editMenuItems },
|
||||
{ key: 'view', label: 'View', items: viewMenuItems },
|
||||
{ key: 'window', label: 'Window', items: windowMenuItems }
|
||||
],
|
||||
[appMenuItems, editMenuItems, fileMenuItems, viewMenuItems, windowMenuItems]
|
||||
)
|
||||
|
||||
return (
|
||||
<Flex
|
||||
align='center'
|
||||
className='electrobun-webkit-app-region-no-drag'
|
||||
style={{ marginRight: '8px', flexShrink: 0, marginLeft: '4px' }}
|
||||
>
|
||||
{menus.map((menu) => {
|
||||
if (menu.key === 'app') {
|
||||
return (
|
||||
<MenuButton
|
||||
key={menu.key}
|
||||
label={
|
||||
<FarmControlLogoSmall
|
||||
style={{
|
||||
fontSize: '46px',
|
||||
height: '16px'
|
||||
}}
|
||||
/>
|
||||
}
|
||||
items={menu.items}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<MenuButton key={menu.key} label={menu.label} items={menu.items} />
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default WindowAppMenu
|
||||
@ -341,7 +341,7 @@ export function getWindowState() {
|
||||
}
|
||||
|
||||
export function handleWindowControl(action) {
|
||||
if (!mainWindow) return
|
||||
if (!mainWindow && action !== 'quit') return
|
||||
|
||||
switch (action) {
|
||||
case 'minimize':
|
||||
@ -375,6 +375,12 @@ export function handleWindowControl(action) {
|
||||
case 'close':
|
||||
mainWindow.close?.()
|
||||
break
|
||||
case 'quit':
|
||||
Utils.quit()
|
||||
break
|
||||
case 'toggle-devtools':
|
||||
mainWindow?.webview?.toggleDevTools?.()
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user