Add screen ratio fixing
This commit is contained in:
@@ -142,11 +142,10 @@ fun TerminalView(
|
|||||||
showContextMenu = false
|
showContextMenu = false
|
||||||
selectionStart = null
|
selectionStart = null
|
||||||
selectionEnd = null
|
selectionEnd = null
|
||||||
val cellWidth = size.width / cols
|
val metrics = calculateGridMetrics(size.width.toFloat(), size.height.toFloat(), cols, rows)
|
||||||
val cellHeight = size.height / rows
|
if (metrics.cellWidth > 0 && metrics.cellHeight > 0) {
|
||||||
if (cellWidth > 0 && cellHeight > 0) {
|
val col = ((startPos.x - metrics.offsetX) / metrics.cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val col = (startPos.x / cellWidth).toInt().coerceIn(0, cols - 1)
|
val row = ((startPos.y - metrics.offsetY) / metrics.cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
val row = (startPos.y / cellHeight).toInt().coerceIn(0, rows - 1)
|
|
||||||
var addr = row * cols + col
|
var addr = row * cols + col
|
||||||
|
|
||||||
val buf = screenBuffer
|
val buf = screenBuffer
|
||||||
@@ -167,17 +166,26 @@ fun TerminalView(
|
|||||||
val activeCursorAddr = currentCursorAddr
|
val activeCursorAddr = currentCursorAddr
|
||||||
if (version == -1L) return@Canvas
|
if (version == -1L) return@Canvas
|
||||||
|
|
||||||
val width = size.width
|
val metrics = calculateGridMetrics(size.width, size.height, cols, rows)
|
||||||
val height = size.height
|
if (metrics.cellWidth <= 0f || metrics.cellHeight <= 0f) return@Canvas
|
||||||
val cellWidth = width / cols
|
|
||||||
val cellHeight = height / rows
|
|
||||||
|
|
||||||
if (cellWidth <= 0 || cellHeight <= 0) return@Canvas
|
val cellWidth = metrics.cellWidth
|
||||||
|
val cellHeight = metrics.cellHeight
|
||||||
|
val offsetX = metrics.offsetX
|
||||||
|
val offsetY = metrics.offsetY
|
||||||
|
|
||||||
val paint = Paint().apply {
|
val paint = Paint().apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
typeface = Typeface.MONOSPACE
|
typeface = Typeface.MONOSPACE
|
||||||
textSize = cellHeight * 0.85f
|
textAlign = Paint.Align.CENTER
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate optimal font size ensuring characters fit cleanly in cell without crowding
|
||||||
|
val baseTextSize = cellHeight * 0.78f
|
||||||
|
paint.textSize = baseTextSize
|
||||||
|
val measuredW = paint.measureText("W")
|
||||||
|
if (measuredW > cellWidth * 0.85f && measuredW > 0f) {
|
||||||
|
paint.textSize = baseTextSize * (cellWidth * 0.85f / measuredW)
|
||||||
}
|
}
|
||||||
|
|
||||||
val buf = screenBuffer
|
val buf = screenBuffer
|
||||||
@@ -194,11 +202,11 @@ fun TerminalView(
|
|||||||
|
|
||||||
val start = selectionStart
|
val start = selectionStart
|
||||||
val end = selectionEnd
|
val end = selectionEnd
|
||||||
if (start != null && end != null) {
|
if (start != null && end != null && cellWidth > 0f && cellHeight > 0f) {
|
||||||
val startCol = (start.x / cellWidth).toInt().coerceIn(0, cols - 1)
|
val startCol = ((start.x - offsetX) / cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val startRow = (start.y / cellHeight).toInt().coerceIn(0, rows - 1)
|
val startRow = ((start.y - offsetY) / cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
val endCol = (end.x / cellWidth).toInt().coerceIn(0, cols - 1)
|
val endCol = ((end.x - offsetX) / cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val endRow = (end.y / cellHeight).toInt().coerceIn(0, rows - 1)
|
val endRow = ((end.y - offsetY) / cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
|
|
||||||
selMinRow = minOf(startRow, endRow)
|
selMinRow = minOf(startRow, endRow)
|
||||||
selMaxRow = maxOf(startRow, endRow)
|
selMaxRow = maxOf(startRow, endRow)
|
||||||
@@ -211,8 +219,8 @@ fun TerminalView(
|
|||||||
val addr = r * cols + c
|
val addr = r * cols + c
|
||||||
if (addr >= totalCells) break
|
if (addr >= totalCells) break
|
||||||
|
|
||||||
val left = c * cellWidth
|
val left = offsetX + c * cellWidth
|
||||||
val top = r * cellHeight
|
val top = offsetY + r * cellHeight
|
||||||
|
|
||||||
var charVal = ' '
|
var charVal = ' '
|
||||||
var fgColor: Color
|
var fgColor: Color
|
||||||
@@ -286,7 +294,7 @@ fun TerminalView(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight selected block range
|
// Draw selection box highlight if selected
|
||||||
val isSelected = r in selMinRow..selMaxRow && c in selMinCol..selMaxCol
|
val isSelected = r in selMinRow..selMaxRow && c in selMinCol..selMaxCol
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
drawRect(
|
drawRect(
|
||||||
@@ -314,7 +322,7 @@ fun TerminalView(
|
|||||||
val symHeight = slot.height
|
val symHeight = slot.height
|
||||||
val rgbArray = slot.getRgbPixels(fgColor.toArgb(), bgColor.toArgb())
|
val rgbArray = slot.getRgbPixels(fgColor.toArgb(), bgColor.toArgb())
|
||||||
if (rgbArray != null && symWidth > 0 && symHeight > 0) {
|
if (rgbArray != null && symWidth > 0 && symHeight > 0) {
|
||||||
val bmp = android.graphics.Bitmap.createBitmap(rgbArray, symWidth, symHeight, android.graphics.Bitmap.Config.ARGB_8888)
|
val bmp = android.graphics.Bitmap.createBitmap(rgbArray as IntArray, symWidth, symHeight, android.graphics.Bitmap.Config.ARGB_8888)
|
||||||
drawContext.canvas.nativeCanvas.drawBitmap(
|
drawContext.canvas.nativeCanvas.drawBitmap(
|
||||||
bmp,
|
bmp,
|
||||||
null,
|
null,
|
||||||
@@ -330,11 +338,12 @@ fun TerminalView(
|
|||||||
paint.color = fgColor.toArgb()
|
paint.color = fgColor.toArgb()
|
||||||
paint.isFakeBoldText = isBold
|
paint.isFakeBoldText = isBold
|
||||||
val fontMetrics = paint.fontMetrics
|
val fontMetrics = paint.fontMetrics
|
||||||
val textY = top + (cellHeight - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top
|
val textX = left + cellWidth / 2f
|
||||||
|
val textY = top + (cellHeight - fontMetrics.bottom + fontMetrics.top) / 2f - fontMetrics.top
|
||||||
|
|
||||||
drawContext.canvas.nativeCanvas.drawText(
|
drawContext.canvas.nativeCanvas.drawText(
|
||||||
charVal.toString(),
|
charVal.toString(),
|
||||||
left + (cellWidth / 4),
|
textX,
|
||||||
textY,
|
textY,
|
||||||
paint
|
paint
|
||||||
)
|
)
|
||||||
@@ -343,8 +352,8 @@ fun TerminalView(
|
|||||||
if (isUnderline) {
|
if (isUnderline) {
|
||||||
drawLine(
|
drawLine(
|
||||||
color = fgColor,
|
color = fgColor,
|
||||||
start = Offset(left, top + cellHeight - 2),
|
start = Offset(left, top + cellHeight - 2f),
|
||||||
end = Offset(left + cellWidth, top + cellHeight - 2),
|
end = Offset(left + cellWidth, top + cellHeight - 2f),
|
||||||
strokeWidth = 2f
|
strokeWidth = 2f
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -353,8 +362,8 @@ fun TerminalView(
|
|||||||
|
|
||||||
// Draw Vector Graphics Plane overlay if present
|
// Draw Vector Graphics Plane overlay if present
|
||||||
if (graphicsPlane != null && graphicsPlane.hasContent()) {
|
if (graphicsPlane != null && graphicsPlane.hasContent()) {
|
||||||
val gridW = (cols * cellWidth).toInt()
|
val gridW = metrics.gridWidth.toInt()
|
||||||
val gridH = (rows * cellHeight).toInt()
|
val gridH = metrics.gridHeight.toInt()
|
||||||
if (gridW > 0 && gridH > 0) {
|
if (gridW > 0 && gridH > 0) {
|
||||||
graphicsPlane.resize(gridW, gridH)
|
graphicsPlane.resize(gridW, gridH)
|
||||||
val rgb = graphicsPlane.rgbBuffer
|
val rgb = graphicsPlane.rgbBuffer
|
||||||
@@ -363,7 +372,7 @@ fun TerminalView(
|
|||||||
drawContext.canvas.nativeCanvas.drawBitmap(
|
drawContext.canvas.nativeCanvas.drawBitmap(
|
||||||
bmp,
|
bmp,
|
||||||
null,
|
null,
|
||||||
android.graphics.RectF(0f, 0f, gridW.toFloat(), gridH.toFloat()),
|
android.graphics.RectF(offsetX, offsetY, offsetX + gridW.toFloat(), offsetY + gridH.toFloat()),
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -425,6 +434,57 @@ fun TerminalView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class TerminalGridMetrics(
|
||||||
|
val cellWidth: Float,
|
||||||
|
val cellHeight: Float,
|
||||||
|
val gridWidth: Float,
|
||||||
|
val gridHeight: Float,
|
||||||
|
val offsetX: Float,
|
||||||
|
val offsetY: Float
|
||||||
|
)
|
||||||
|
|
||||||
|
fun calculateGridMetrics(
|
||||||
|
viewWidth: Float,
|
||||||
|
viewHeight: Float,
|
||||||
|
cols: Int,
|
||||||
|
rows: Int
|
||||||
|
): TerminalGridMetrics {
|
||||||
|
if (viewWidth <= 0f || viewHeight <= 0f || cols <= 0 || rows <= 0) {
|
||||||
|
return TerminalGridMetrics(0f, 0f, 0f, 0f, 0f, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
val rawCellW = viewWidth / cols
|
||||||
|
val rawCellH = viewHeight / rows
|
||||||
|
val currentRatio = rawCellW / rawCellH
|
||||||
|
|
||||||
|
// Target terminal cell aspect ratio is roughly 0.50 to 0.60 (width / height)
|
||||||
|
val targetMinRatio = 0.48f
|
||||||
|
val targetMaxRatio = 0.60f
|
||||||
|
|
||||||
|
val cellW: Float
|
||||||
|
val cellH: Float
|
||||||
|
|
||||||
|
if (currentRatio > targetMaxRatio) {
|
||||||
|
// Screen is wider than ideal aspect ratio -> clamp width, pillarbox (equal blank bars left/right)
|
||||||
|
cellH = rawCellH
|
||||||
|
cellW = rawCellH * targetMaxRatio
|
||||||
|
} else if (currentRatio < targetMinRatio) {
|
||||||
|
// Screen is taller than ideal aspect ratio -> clamp height, letterbox (equal blank bars top/bottom)
|
||||||
|
cellW = rawCellW
|
||||||
|
cellH = rawCellW / targetMinRatio
|
||||||
|
} else {
|
||||||
|
cellW = rawCellW
|
||||||
|
cellH = rawCellH
|
||||||
|
}
|
||||||
|
|
||||||
|
val gridW = cols * cellW
|
||||||
|
val gridH = rows * cellH
|
||||||
|
val offX = (viewWidth - gridW) / 2f
|
||||||
|
val offY = (viewHeight - gridH) / 2f
|
||||||
|
|
||||||
|
return TerminalGridMetrics(cellW, cellH, gridW, gridH, offX, offY)
|
||||||
|
}
|
||||||
|
|
||||||
private fun copySelection(
|
private fun copySelection(
|
||||||
start: Offset?,
|
start: Offset?,
|
||||||
end: Offset?,
|
end: Offset?,
|
||||||
@@ -436,13 +496,15 @@ private fun copySelection(
|
|||||||
) {
|
) {
|
||||||
if (start != null && end != null && screenBuffer != null && cols > 0 && rows > 0) {
|
if (start != null && end != null && screenBuffer != null && cols > 0 && rows > 0) {
|
||||||
val displayMetrics = context.resources.displayMetrics
|
val displayMetrics = context.resources.displayMetrics
|
||||||
val cellWidth = displayMetrics.widthPixels.toFloat() / cols
|
val viewW = displayMetrics.widthPixels.toFloat()
|
||||||
val cellHeight = displayMetrics.heightPixels.toFloat() / rows
|
val viewH = displayMetrics.heightPixels.toFloat()
|
||||||
|
val metrics = calculateGridMetrics(viewW, viewH, cols, rows)
|
||||||
|
if (metrics.cellWidth <= 0f || metrics.cellHeight <= 0f) return
|
||||||
|
|
||||||
val startCol = (start.x / cellWidth).toInt().coerceIn(0, cols - 1)
|
val startCol = ((start.x - metrics.offsetX) / metrics.cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val startRow = (start.y / cellHeight).toInt().coerceIn(0, rows - 1)
|
val startRow = ((start.y - metrics.offsetY) / metrics.cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
val endCol = (end.x / cellWidth).toInt().coerceIn(0, cols - 1)
|
val endCol = ((end.x - metrics.offsetX) / metrics.cellWidth).toInt().coerceIn(0, cols - 1)
|
||||||
val endRow = (end.y / cellHeight).toInt().coerceIn(0, rows - 1)
|
val endRow = ((end.y - metrics.offsetY) / metrics.cellHeight).toInt().coerceIn(0, rows - 1)
|
||||||
|
|
||||||
val minRow = minOf(startRow, endRow)
|
val minRow = minOf(startRow, endRow)
|
||||||
val maxRow = maxOf(startRow, endRow)
|
val maxRow = maxOf(startRow, endRow)
|
||||||
|
|||||||
Reference in New Issue
Block a user