Refactor window management for macOS and Windows
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 `createWindow` and `createMainWindow` functions to conditionally set title bar styles and traffic light positions based on the operating system. - Enhanced window layout synchronization by introducing `syncMaximizedWindowFrame` and improving the logic in `isWindowWorkAreaMaximized` to account for maximized window frames. - Added functions to calculate DWM border padding and adjusted frame handling to ensure proper window sizing and positioning across different platforms.
This commit is contained in:
parent
cfb8c6ede1
commit
2a72697129
@ -205,8 +205,12 @@ export function createWindow() {
|
|||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 800,
|
||||||
frame: false,
|
frame: false,
|
||||||
titleBarStyle: 'hiddenInset',
|
...(process.platform === 'darwin'
|
||||||
trafficLightPosition: { x: 14, y: 12 },
|
? {
|
||||||
|
titleBarStyle: 'hiddenInset',
|
||||||
|
trafficLightPosition: { x: 14, y: 12 }
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
backgroundColor: '#141414',
|
backgroundColor: '#141414',
|
||||||
icon: path.join(__dirname, './logo512.png'),
|
icon: path.join(__dirname, './logo512.png'),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import {
|
|||||||
import { sendToRenderer, setMessageSender } from './notify.js'
|
import { sendToRenderer, setMessageSender } from './notify.js'
|
||||||
import {
|
import {
|
||||||
clampWindowToWorkArea,
|
clampWindowToWorkArea,
|
||||||
isWindowWorkAreaMaximized
|
isWindowWorkAreaMaximized,
|
||||||
|
syncMaximizedWindowFrame
|
||||||
} from './windows-work-area.js'
|
} from './windows-work-area.js'
|
||||||
|
|
||||||
const isMacOS = process.platform === 'darwin'
|
const isMacOS = process.platform === 'darwin'
|
||||||
@ -158,6 +159,11 @@ function applyStartupWindowState(window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncWindowsWebviewLayout(window) {
|
function syncWindowsWebviewLayout(window) {
|
||||||
|
if (isWindowWorkAreaMaximized(window)) {
|
||||||
|
syncMaximizedWindowFrame(window)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!window?.getSize || !window?.setSize) {
|
if (!window?.getSize || !window?.setSize) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -172,7 +178,7 @@ function syncWindowsWebviewLayout(window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleWindowsWindowChange(window) {
|
function handleWindowsWindowChange(window) {
|
||||||
if (clampWindowToWorkArea(window)) {
|
if (clampWindowToWorkArea(window) || isWindowWorkAreaMaximized(window)) {
|
||||||
syncWindowsWebviewLayout(window)
|
syncWindowsWebviewLayout(window)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,10 +252,15 @@ export async function createMainWindow(rpc) {
|
|||||||
title: 'Farm Control',
|
title: 'Farm Control',
|
||||||
url,
|
url,
|
||||||
rpc,
|
rpc,
|
||||||
titleBarStyle: 'hiddenInset',
|
|
||||||
...(isMacOS
|
...(isMacOS
|
||||||
? { transparent: true, trafficLightOffset: MAC_TRAFFIC_LIGHT_OFFSET }
|
? {
|
||||||
: {}),
|
titleBarStyle: 'hiddenInset',
|
||||||
|
transparent: true,
|
||||||
|
trafficLightOffset: MAC_TRAFFIC_LIGHT_OFFSET
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
titleBarStyle: 'hidden'
|
||||||
|
}),
|
||||||
frame: {
|
frame: {
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 800,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Screen } from 'electrobun/bun'
|
import { Screen } from 'electrobun/bun'
|
||||||
|
|
||||||
const FRAME_TOLERANCE_PX = 4
|
const FRAME_TOLERANCE_PX = 4
|
||||||
|
const BASE_DWM_BORDER_PX = 7
|
||||||
|
|
||||||
function framesMatch(a, b, tolerance = FRAME_TOLERANCE_PX) {
|
function framesMatch(a, b, tolerance = FRAME_TOLERANCE_PX) {
|
||||||
return (
|
return (
|
||||||
@ -34,6 +35,33 @@ function getDisplayForFrame(frame) {
|
|||||||
return match ?? Screen.getPrimaryDisplay()
|
return match ?? Screen.getPrimaryDisplay()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDwmBorderPadding(display) {
|
||||||
|
const scaleFactor = display?.scaleFactor ?? 1
|
||||||
|
return Math.round(BASE_DWM_BORDER_PX * scaleFactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaximizedFrame(workArea, display) {
|
||||||
|
const borderPx = getDwmBorderPadding(display)
|
||||||
|
|
||||||
|
// Windows keeps invisible resize borders on the left, right, and bottom when
|
||||||
|
// maximized. Extend the outer frame so the webview fills the visible area.
|
||||||
|
return {
|
||||||
|
x: workArea.x - borderPx,
|
||||||
|
y: workArea.y,
|
||||||
|
width: workArea.width + borderPx * 2,
|
||||||
|
height: workArea.height + borderPx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function frameCoversWorkArea(frame, workArea) {
|
||||||
|
return (
|
||||||
|
frame.x <= workArea.x + FRAME_TOLERANCE_PX &&
|
||||||
|
frame.y <= workArea.y + FRAME_TOLERANCE_PX &&
|
||||||
|
frame.x + frame.width >= workArea.x + workArea.width - FRAME_TOLERANCE_PX &&
|
||||||
|
frame.y + frame.height >= workArea.y + workArea.height - FRAME_TOLERANCE_PX
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function frameCoversMonitor(frame, bounds) {
|
function frameCoversMonitor(frame, bounds) {
|
||||||
return (
|
return (
|
||||||
frame.x <= bounds.x + FRAME_TOLERANCE_PX &&
|
frame.x <= bounds.x + FRAME_TOLERANCE_PX &&
|
||||||
@ -49,8 +77,14 @@ export function isWindowWorkAreaMaximized(window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const frame = window.getFrame()
|
const frame = window.getFrame()
|
||||||
const { workArea } = getDisplayForFrame(frame)
|
const display = getDisplayForFrame(frame)
|
||||||
return framesMatch(frame, workArea)
|
const maximizedFrame = getMaximizedFrame(display.workArea, display)
|
||||||
|
|
||||||
|
return (
|
||||||
|
framesMatch(frame, display.workArea) ||
|
||||||
|
framesMatch(frame, maximizedFrame) ||
|
||||||
|
frameCoversWorkArea(frame, display.workArea)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clampWindowToWorkArea(window) {
|
export function clampWindowToWorkArea(window) {
|
||||||
@ -69,15 +103,60 @@ export function clampWindowToWorkArea(window) {
|
|||||||
|
|
||||||
const display = getDisplayForFrame(frame)
|
const display = getDisplayForFrame(frame)
|
||||||
const { bounds, workArea } = display
|
const { bounds, workArea } = display
|
||||||
|
const maximizedFrame = getMaximizedFrame(workArea, display)
|
||||||
|
|
||||||
if (framesMatch(frame, workArea)) {
|
if (framesMatch(frame, maximizedFrame)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frameCoversMonitor(frame, bounds)) {
|
if (
|
||||||
|
!frameCoversWorkArea(frame, workArea) &&
|
||||||
|
!frameCoversMonitor(frame, bounds)
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
window.setFrame(workArea.x, workArea.y, workArea.width, workArea.height)
|
window.setFrame(
|
||||||
|
maximizedFrame.x,
|
||||||
|
maximizedFrame.y,
|
||||||
|
maximizedFrame.width,
|
||||||
|
maximizedFrame.height
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function syncMaximizedWindowFrame(window) {
|
||||||
|
if (!window?.getFrame || !window?.setFrame) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.isFullScreen?.()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame = window.getFrame()
|
||||||
|
if (!frame.width || !frame.height) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const display = getDisplayForFrame(frame)
|
||||||
|
if (
|
||||||
|
!frameCoversWorkArea(frame, display.workArea) &&
|
||||||
|
!frameCoversMonitor(frame, display.bounds)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const maximizedFrame = getMaximizedFrame(display.workArea, display)
|
||||||
|
if (framesMatch(frame, maximizedFrame)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setFrame(
|
||||||
|
maximizedFrame.x,
|
||||||
|
maximizedFrame.y,
|
||||||
|
maximizedFrame.width,
|
||||||
|
maximizedFrame.height
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user