Skip to content

Commit e23bd23

Browse files
committed
feat(SquirrelPanel): 全屏模式
內容超出屏幕空間時,自動縮放文字,候選窗居中。
1 parent 11e97dd commit e23bd23

1 file changed

Lines changed: 102 additions & 57 deletions

File tree

sources/SquirrelPanel.swift

Lines changed: 102 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -367,93 +367,138 @@ private extension SquirrelPanel {
367367
self.appearance = NSAppearance(named: .aqua)
368368
}
369369

370-
// Break line if the text is too long, based on screen size.
370+
view.textView.textContainerInset = theme.edgeInset
371+
371372
let textWidth = maxTextWidth()
372-
let maxTextHeight = vertical ? screenRect.width - theme.edgeInset.width * 2 : screenRect.height - theme.edgeInset.height * 2
373-
view.textContainer.size = NSSize(width: textWidth, height: maxTextHeight)
373+
// 高度設置爲無窮大,放開限制,讓超大文本完全測量出真實自然高度
374+
view.textContainer.size = NSSize(width: textWidth, height: .greatestFiniteMagnitude)
375+
376+
// 強制完成排版,並歸零 bounds
377+
view.textLayoutManager.ensureLayout(for: view.textLayoutManager.documentRange)
378+
view.textView.bounds.origin = .zero
374379

375-
var panelRect = NSRect.zero
376-
// in vertical mode, the width and height are interchanged
377380
var contentRect = view.contentRect
378-
if theme.memorizeSize && (vertical && position.midY / screenRect.height < 0.5) ||
379-
(vertical && position.minX + max(contentRect.width, maxHeight) + theme.edgeInset.width * 2 > screenRect.maxX) {
380-
if contentRect.width >= maxHeight {
381-
maxHeight = contentRect.width
382-
} else {
383-
contentRect.size.width = maxHeight
384-
view.textContainer.size = NSSize(width: maxHeight, height: maxTextHeight)
385-
}
386-
}
387381

382+
// 計算出「不加限制時」需要的自然面板巨型尺寸
383+
var naturalPanelSize = NSSize.zero
388384
if vertical {
389-
panelRect.size = NSSize(width: min(0.95 * screenRect.width, contentRect.height + theme.edgeInset.height * 2),
390-
height: min(0.95 * screenRect.height, contentRect.width + theme.edgeInset.width * 2) + theme.pagingOffset)
391-
392-
// To avoid jumping up and down while typing, use the lower screen when
393-
// typing on upper, and vice versa
394-
if position.midY / screenRect.height >= 0.5 {
395-
panelRect.origin.y = position.minY - SquirrelTheme.offsetHeight - panelRect.height + theme.pagingOffset
396-
} else {
397-
panelRect.origin.y = position.maxY + SquirrelTheme.offsetHeight
398-
}
399-
// Make the first candidate fixed at the left of cursor
400-
panelRect.origin.x = position.minX - panelRect.width - SquirrelTheme.offsetHeight
401-
if view.preeditRange.length > 0, let preeditTextRange = view.convert(range: view.preeditRange) {
402-
let preeditRect = view.contentRect(range: preeditTextRange)
403-
panelRect.origin.x += preeditRect.height + theme.edgeInset.width
404-
}
385+
naturalPanelSize.width = contentRect.height + theme.edgeInset.height * 2
386+
naturalPanelSize.height = contentRect.width + theme.edgeInset.width * 2 + theme.pagingOffset
405387
} else {
406-
panelRect.size = NSSize(width: min(0.95 * screenRect.width, contentRect.width + theme.edgeInset.width * 2),
407-
height: min(0.95 * screenRect.height, contentRect.height + theme.edgeInset.height * 2))
408-
panelRect.size.width += theme.pagingOffset
409-
panelRect.origin = NSPoint(x: position.minX - theme.pagingOffset, y: position.minY - SquirrelTheme.offsetHeight - panelRect.height)
388+
naturalPanelSize.width = contentRect.width + theme.edgeInset.width * 2 + theme.pagingOffset
389+
naturalPanelSize.height = contentRect.height + theme.edgeInset.height * 2
410390
}
411-
if panelRect.maxX > screenRect.maxX {
412-
panelRect.origin.x = screenRect.maxX - panelRect.width
413-
}
414-
if panelRect.minX < screenRect.minX {
415-
panelRect.origin.x = screenRect.minX
416-
}
417-
if panelRect.minY < screenRect.minY {
391+
392+
// 屏幕最大可用範圍(留白 5%)
393+
let maxAllowedWidth = screenRect.width * 0.95
394+
let maxAllowedHeight = screenRect.height * 0.95
395+
396+
// 判斷是否需要觸發「全屏模式」
397+
let requiresFullScreen = naturalPanelSize.width > maxAllowedWidth || naturalPanelSize.height > maxAllowedHeight
398+
399+
var panelRect = NSRect.zero
400+
401+
if requiresFullScreen {
402+
// --- 全屏縮放模式 ---
403+
let scaleX = maxAllowedWidth / naturalPanelSize.width
404+
let scaleY = maxAllowedHeight / naturalPanelSize.height
405+
let scale = min(scaleX, scaleY) // 保持等比縮小
406+
407+
// 窗口實際物理大小被縮小
408+
panelRect.size = NSSize(width: naturalPanelSize.width * scale, height: naturalPanelSize.height * scale)
409+
410+
// 屏幕正中央對齊
411+
panelRect.origin = NSPoint(
412+
x: screenRect.minX + (screenRect.width - panelRect.width) / 2,
413+
y: screenRect.minY + (screenRect.height - panelRect.height) / 2
414+
)
415+
416+
maxHeight = 0 // 重置記憶尺寸緩存
417+
} else {
418+
// --- 常規跟隨光標模式 ---
419+
// Apply memorizeSize
420+
if theme.memorizeSize && (vertical && position.midY / screenRect.height < 0.5) ||
421+
(vertical && position.minX + max(contentRect.width, maxHeight) + theme.edgeInset.width * 2 > screenRect.maxX) {
422+
if contentRect.width >= maxHeight {
423+
maxHeight = contentRect.width
424+
} else {
425+
contentRect.size.width = maxHeight
426+
// 需要根據記憶寬度更新自然尺寸
427+
if vertical {
428+
naturalPanelSize.height = contentRect.width + theme.edgeInset.width * 2 + theme.pagingOffset
429+
} else {
430+
naturalPanelSize.width = contentRect.width + theme.edgeInset.width * 2 + theme.pagingOffset
431+
}
432+
}
433+
}
434+
435+
panelRect.size = naturalPanelSize
436+
418437
if vertical {
419-
panelRect.origin.y = screenRect.minY
438+
// To avoid jumping up and down while typing
439+
if position.midY / screenRect.height >= 0.5 {
440+
panelRect.origin.y = position.minY - SquirrelTheme.offsetHeight - panelRect.height + theme.pagingOffset
441+
} else {
442+
panelRect.origin.y = position.maxY + SquirrelTheme.offsetHeight
443+
}
444+
panelRect.origin.x = position.minX - panelRect.width - SquirrelTheme.offsetHeight
445+
if view.preeditRange.length > 0, let preeditTextRange = view.convert(range: view.preeditRange) {
446+
let preeditRect = view.contentRect(range: preeditTextRange)
447+
panelRect.origin.x += preeditRect.height + theme.edgeInset.width
448+
}
420449
} else {
421-
panelRect.origin.y = position.maxY + SquirrelTheme.offsetHeight
450+
panelRect.origin = NSPoint(x: position.minX - theme.pagingOffset, y: position.minY - SquirrelTheme.offsetHeight - panelRect.height)
422451
}
452+
453+
// 常規模式下的邊界限制
454+
if panelRect.maxX > screenRect.maxX { panelRect.origin.x = screenRect.maxX - panelRect.width }
455+
if panelRect.minX < screenRect.minX { panelRect.origin.x = screenRect.minX }
456+
if panelRect.minY < screenRect.minY {
457+
if vertical { panelRect.origin.y = screenRect.minY } else { panelRect.origin.y = position.maxY + SquirrelTheme.offsetHeight }
458+
}
459+
if panelRect.maxY > screenRect.maxY { panelRect.origin.y = screenRect.maxY - panelRect.height }
460+
if panelRect.minY < screenRect.minY { panelRect.origin.y = screenRect.minY }
423461
}
424-
if panelRect.maxY > screenRect.maxY {
425-
panelRect.origin.y = screenRect.maxY - panelRect.height
426-
}
427-
if panelRect.minY < screenRect.minY {
428-
panelRect.origin.y = screenRect.minY
429-
}
462+
430463
self.setFrame(panelRect, display: true)
431464

465+
// contentView 的 frame 決定了它在窗口上的物理大小;
466+
// contentView 的 bounds 決定了它內部的投影座標系。
467+
// 將 bounds 設爲 naturalPanelSize(自然尺寸),視圖內部畫的一切東西就會自動被縮小到窗口(frame)裏
468+
contentView!.frame = NSRect(origin: .zero, size: panelRect.size)
469+
contentView!.bounds = NSRect(origin: .zero, size: naturalPanelSize)
470+
432471
// rotate the view, the core in vertical mode!
433472
if vertical {
434473
contentView!.boundsRotation = -90
435-
contentView!.setBoundsOrigin(NSPoint(x: 0, y: panelRect.width))
474+
contentView!.setBoundsOrigin(NSPoint(x: 0, y: naturalPanelSize.width))
436475
} else {
437476
contentView!.boundsRotation = 0
438477
contentView!.setBoundsOrigin(.zero)
439478
}
479+
440480
view.textView.boundsRotation = 0
441481
view.textView.setBoundsOrigin(.zero)
442482

443-
view.frame = contentView!.bounds
444-
view.textView.frame = contentView!.bounds
445-
view.textView.frame.size.width -= theme.pagingOffset
446-
view.textView.frame.origin.x += theme.pagingOffset
447-
view.textView.textContainerInset = theme.edgeInset
483+
// 下層所有組件的幾何尺寸全部基於自然巨型尺寸 (naturalPanelSize)
484+
let subviewFrame = NSRect(origin: .zero, size: naturalPanelSize)
485+
view.frame = subviewFrame
486+
487+
var textFrame = subviewFrame
488+
textFrame.size.width -= theme.pagingOffset
489+
textFrame.origin.x += theme.pagingOffset
490+
view.textView.frame = textFrame
448491

449492
if theme.translucency {
450-
back.frame = contentView!.bounds
451-
back.frame.size.width += theme.pagingOffset
493+
var backFrame = subviewFrame
494+
backFrame.size.width += theme.pagingOffset
495+
back.frame = backFrame
452496
back.appearance = NSApp.effectiveAppearance
453497
back.isHidden = false
454498
} else {
455499
back.isHidden = true
456500
}
501+
457502
alphaValue = theme.alpha
458503
invalidateShadow()
459504
orderFront(nil)

0 commit comments

Comments
 (0)