extends RefCounted ## STB-RESIZE-01 的真实窗口适配器(PlayableFlow.window)。 ## ## macOS Retina 下 DisplayServer 的窗口尺寸是物理像素,逻辑尺寸 = 物理像素 / 屏幕缩放。 ## 流程只按逻辑尺寸判定;物理像素、缩放和视口尺寸一并写进事件,Retina 的 2x 不是分辨率错误。 ## 请求尺寸超出当前屏幕可用区域(含标题栏)时如实拒绝(request_logical_size 返回 false), ## 由流程记为 BLOCKED,不缩小目标冒充通过。headless / 全屏 / 最大化窗口不可调整。 var _viewport: Viewport func _init(viewport: Viewport = null) -> void: _viewport = viewport func availability() -> Dictionary: if DisplayServer.get_name() == "headless": return {"ok": false, "reason": "headless display server has no window"} match DisplayServer.window_get_mode(): DisplayServer.WINDOW_MODE_FULLSCREEN, DisplayServer.WINDOW_MODE_EXCLUSIVE_FULLSCREEN: return {"ok": false, "reason": "window is fullscreen; logical size switching needs windowed mode"} DisplayServer.WINDOW_MODE_MAXIMIZED: return {"ok": false, "reason": "window is maximized; logical size switching needs windowed mode"} DisplayServer.WINDOW_MODE_MINIMIZED: return {"ok": false, "reason": "window is minimized"} if screen_scale() <= 0.0: return {"ok": false, "reason": "screen scale unavailable"} return {"ok": true, "reason": ""} func screen_scale() -> float: return DisplayServer.screen_get_scale(DisplayServer.window_get_current_screen()) func physical_size() -> Vector2i: return DisplayServer.window_get_size() func logical_size() -> Vector2i: var scale := screen_scale() var size := physical_size() if scale <= 0.0: return size return Vector2i(roundi(size.x / scale), roundi(size.y / scale)) func viewport_size() -> Vector2i: if _viewport == null or not is_instance_valid(_viewport): return Vector2i.ZERO return Vector2i(_viewport.get_visible_rect().size) ## Returns false (no request sent) when the size cannot fit the usable screen area. func request_logical_size(size: Vector2i) -> bool: var scale := screen_scale() if scale <= 0.0 or size.x <= 0 or size.y <= 0: return false var target := Vector2i(roundi(size.x * scale), roundi(size.y * scale)) var screen := DisplayServer.window_get_current_screen() var usable := DisplayServer.screen_get_usable_rect(screen) var decorations := DisplayServer.window_get_size_with_decorations() - DisplayServer.window_get_size() if target.x + decorations.x > usable.size.x or target.y + decorations.y > usable.size.y: return false DisplayServer.window_set_size(target) # Keep the whole window on the usable area so the OS does not clamp the size back. var position := DisplayServer.window_get_position() var clamped := Vector2i(clampi(position.x, usable.position.x, usable.end.x - target.x - decorations.x), clampi(position.y, usable.position.y, usable.end.y - target.y - decorations.y)) if clamped != position: DisplayServer.window_set_position(clamped) return true