56 lines
2.2 KiB
Go
56 lines
2.2 KiB
Go
// Fenrir console screen reader.
|
|
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
// more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
|
|
package screen
|
|
|
|
import (
|
|
"slices"
|
|
"unicode"
|
|
)
|
|
|
|
// A TUI can scroll only its message area, leaving headers, status and input
|
|
// rows fixed. Suppress the longest contiguous shifted overlap, while leaving
|
|
// every other changed row available for normal feedback. Partial overlaps
|
|
// require two nonblank rows: a lone repeated label or a blank area is ambiguous.
|
|
func Scrolled_overlap(previous, next Snapshot) (bestStart, bestEnd int) {
|
|
// Match a whole-screen scroll first, including sparse screens. Ignore
|
|
// whitespace-only overlaps, which cannot establish that anything scrolled.
|
|
firstText := slices.IndexFunc(next.Cells, func(cell rune) bool { return !unicode.IsSpace(cell) })
|
|
for shift := 1; shift < next.Rows && firstText >= 0 && firstText < (next.Rows-shift)*next.Cols; shift++ {
|
|
if slices.Equal(previous.Cells[shift*next.Cols:], next.Cells[:(next.Rows-shift)*next.Cols]) {
|
|
return 0, next.Rows - shift
|
|
}
|
|
}
|
|
bestText := 0
|
|
for shift := 1; shift < next.Rows; shift++ {
|
|
start, textRows := 0, 0
|
|
for row := 0; row <= next.Rows-shift; row++ {
|
|
if row < next.Rows-shift && slices.Equal(previous.Line(row+shift), next.Line(row)) {
|
|
if !slices.Equal(previous.Line(row), next.Line(row)) && slices.ContainsFunc(next.Line(row), func(cell rune) bool { return !unicode.IsSpace(cell) }) {
|
|
textRows++
|
|
}
|
|
continue
|
|
}
|
|
if textRows >= 2 && textRows > bestText {
|
|
bestStart, bestEnd, bestText = start, row, textRows
|
|
}
|
|
start, textRows = row+1, 0
|
|
}
|
|
}
|
|
return bestStart, bestEnd
|
|
}
|