from __future__ import annotations from dataclasses import dataclass from app.qt_compat import QRectF from app.config.models import InfinityMirrorConfig, TileConfig @dataclass(frozen=True) class PreviewLayout: canvas_rect: QRectF tile_rects: dict[str, QRectF] def compute_preview_layout(widget_rect: QRectF, config: InfinityMirrorConfig) -> PreviewLayout: rows = config.logical_display.rows cols = config.logical_display.cols outer = QRectF(widget_rect).adjusted(28, 28, -28, -28) gap = min(22.0, max(10.0, min(outer.width() / 48.0, outer.height() / 22.0))) tile_aspect = tile_aspect_ratio(config) usable_width = max(1.0, outer.width() - gap * (cols - 1)) usable_height = max(1.0, outer.height() - gap * (rows - 1)) tile_width = usable_width / max(1, cols) tile_height = tile_width / tile_aspect if tile_height * rows > usable_height: tile_height = usable_height / max(1, rows) tile_width = tile_height * tile_aspect shell_padding = max(18.0, min(tile_width, tile_height) * 0.16) grid_width = tile_width * cols + gap * (cols - 1) grid_height = tile_height * rows + gap * (rows - 1) left = outer.left() + (outer.width() - grid_width) / 2.0 top = outer.top() + (outer.height() - grid_height) / 2.0 canvas_rect = QRectF(left - shell_padding, top - shell_padding, grid_width + shell_padding * 2.0, grid_height + shell_padding * 2.0) tile_rects: dict[str, QRectF] = {} for tile in config.sorted_tiles(): x = left + (tile.col - 1) * (tile_width + gap) y = top + (tile.row - 1) * (tile_height + gap) tile_rects[tile.tile_id] = QRectF(x, y, tile_width, tile_height) return PreviewLayout(canvas_rect=canvas_rect, tile_rects=tile_rects) def tile_aspect_ratio(config: InfinityMirrorConfig) -> float: logical_display = config.logical_display tile_width = max(1.0, float(logical_display.tile_width)) tile_height = max(1.0, float(logical_display.tile_height)) return max(0.55, min(1.8, tile_width / tile_height)) def segment_display_rect(tile: TileConfig, rect: QRectF) -> QRectF: tile_width = max(0.001, tile.x1 - tile.x0) tile_height = max(0.001, tile.y1 - tile.y0) aspect_ratio = max(0.5, min(1.8, tile_width / tile_height)) base = min(rect.width(), rect.height()) margin = max(6.0, base * 0.07) available_rect = rect.adjusted(margin, margin, -margin, -margin) fitted_width = available_rect.width() fitted_height = fitted_width / aspect_ratio if fitted_height > available_rect.height(): fitted_height = available_rect.height() fitted_width = fitted_height * aspect_ratio left = available_rect.left() + (available_rect.width() - fitted_width) / 2.0 top = available_rect.top() + (available_rect.height() - fitted_height) / 2.0 return QRectF(left, top, fitted_width, fitted_height)