Fix lines in last line not rendered when scrolled up
Instead of creating the rectangles for lines after the line is completed, the rectangle is now initialized as soon as it is started. Then when following cells also contain the same line type, the rectangle is updated. This resolves the problem of having to finish the last line when it ends in the last non-empty column in the last line, since the render iterator only returns non-empty cells and we never get the information that the underline has ended. Fixes #2680.
This commit is contained in:
parent
e1892ee92a
commit
ddee14a6ef
|
@ -511,7 +511,7 @@ impl Display {
|
||||||
|
|
||||||
{
|
{
|
||||||
let glyph_cache = &mut self.glyph_cache;
|
let glyph_cache = &mut self.glyph_cache;
|
||||||
let mut rects = Rects::new(&metrics, &size_info);
|
let mut rects = Rects::new();
|
||||||
|
|
||||||
// Draw grid
|
// Draw grid
|
||||||
{
|
{
|
||||||
|
@ -521,7 +521,7 @@ impl Display {
|
||||||
// Iterate over all non-empty cells in the grid
|
// Iterate over all non-empty cells in the grid
|
||||||
for cell in grid_cells {
|
for cell in grid_cells {
|
||||||
// Update underline/strikeout
|
// Update underline/strikeout
|
||||||
rects.update_lines(&size_info, &cell);
|
rects.update_lines(&cell, &size_info, &metrics);
|
||||||
|
|
||||||
// Draw the cell
|
// Draw the cell
|
||||||
api.render_cell(cell, glyph_cache);
|
api.render_cell(cell, glyph_cache);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use font::Metrics;
|
use font::Metrics;
|
||||||
|
|
||||||
use crate::index::Point;
|
use crate::index::Point;
|
||||||
|
@ -32,125 +34,104 @@ impl<T> Rect<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Line {
|
struct Line {
|
||||||
flag: Flags,
|
rect: Rect<f32>,
|
||||||
range: Option<(RenderableCell, Point)>,
|
start: Point,
|
||||||
|
color: Rgb,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Line {
|
impl Line {
|
||||||
fn new(flag: Flags) -> Self {
|
/// Create a line that starts on the left of `cell` and is one cell wide
|
||||||
Self { flag, range: None }
|
fn from_cell(cell: &RenderableCell, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Line {
|
||||||
|
let cell_x = cell.column.0 as f32 * size.cell_width;
|
||||||
|
|
||||||
|
let (position, mut height) = match flag {
|
||||||
|
Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness),
|
||||||
|
Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness),
|
||||||
|
_ => unimplemented!("Invalid flag for cell line drawing specified"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure lines are always visible
|
||||||
|
height = height.max(1.);
|
||||||
|
|
||||||
|
let cell_bottom = (cell.line.0 as f32 + 1.) * size.cell_height;
|
||||||
|
let baseline = cell_bottom + metrics.descent;
|
||||||
|
|
||||||
|
let mut y = baseline - position - height / 2.;
|
||||||
|
let max_y = cell_bottom - height;
|
||||||
|
if y > max_y {
|
||||||
|
y = max_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rect = Rect::new(cell_x + size.padding_x, y + size.padding_y, size.cell_width, height);
|
||||||
|
|
||||||
|
Self { start: cell.into(), color: cell.fg, rect }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_end(&mut self, end: Point, size: &SizeInfo) {
|
||||||
|
self.rect.width = (end.col + 1 - self.start.col).0 as f32 * size.cell_width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rects for underline, strikeout and more.
|
/// Rects for underline, strikeout and more.
|
||||||
pub struct Rects<'a> {
|
#[derive(Default)]
|
||||||
inner: Vec<(Rect<f32>, Rgb)>,
|
pub struct Rects {
|
||||||
active_lines: Vec<Line>,
|
inner: HashMap<Flags, Vec<Line>>,
|
||||||
metrics: &'a Metrics,
|
|
||||||
size: &'a SizeInfo,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Rects<'a> {
|
impl Rects {
|
||||||
pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self {
|
pub fn new() -> Self {
|
||||||
let active_lines = vec![Line::new(Flags::UNDERLINE), Line::new(Flags::STRIKEOUT)];
|
Self::default()
|
||||||
Self { inner: Vec::new(), active_lines, metrics, size }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the stored rects to rectangles for the renderer.
|
/// Convert the stored rects to rectangles for the renderer.
|
||||||
pub fn rects(&self) -> &Vec<(Rect<f32>, Rgb)> {
|
pub fn rects(&self) -> Vec<(Rect<f32>, Rgb)> {
|
||||||
&self.inner
|
self.inner
|
||||||
|
.iter()
|
||||||
|
.map(|(_, lines)| lines)
|
||||||
|
.flatten()
|
||||||
|
.map(|line| (line.rect, line.color))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the stored lines with the next cell info.
|
/// Update the stored lines with the next cell info.
|
||||||
pub fn update_lines(&mut self, size_info: &SizeInfo, cell: &RenderableCell) {
|
pub fn update_lines(&mut self, cell: &RenderableCell, size: &SizeInfo, metrics: &Metrics) {
|
||||||
for line in self.active_lines.iter_mut() {
|
for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] {
|
||||||
match line.range {
|
if !cell.flags.contains(*flag) {
|
||||||
// Check for end if line is present
|
continue;
|
||||||
Some((ref mut start, ref mut end)) => {
|
}
|
||||||
// No change in line
|
|
||||||
if cell.line == start.line
|
|
||||||
&& cell.flags.contains(line.flag)
|
|
||||||
&& cell.fg == start.fg
|
|
||||||
&& cell.column == end.col + 1
|
|
||||||
{
|
|
||||||
if size_info.cols() == cell.column && size_info.lines() == cell.line {
|
|
||||||
// Add the last rect if we've reached the end of the terminal
|
|
||||||
self.inner.push(create_rect(
|
|
||||||
&start,
|
|
||||||
cell.into(),
|
|
||||||
line.flag,
|
|
||||||
&self.metrics,
|
|
||||||
&self.size,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Update the length of the line
|
|
||||||
*end = cell.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
// Check if there's an active line
|
||||||
}
|
if let Some(line) = self.inner.get_mut(flag).and_then(|lines| lines.last_mut()) {
|
||||||
|
if cell.line == line.start.line && cell.fg == line.color {
|
||||||
|
// Update the length of the line
|
||||||
|
line.update_end(cell.into(), size);
|
||||||
|
|
||||||
self.inner.push(create_rect(start, *end, line.flag, &self.metrics, &self.size));
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start a new line if the flag is present
|
// Start new line if there currently is none
|
||||||
if cell.flags.contains(line.flag) {
|
let rect = Line::from_cell(cell, *flag, metrics, size);
|
||||||
*start = cell.clone();
|
match self.inner.get_mut(flag) {
|
||||||
*end = cell.into();
|
Some(lines) => lines.push(rect),
|
||||||
} else {
|
|
||||||
line.range = None;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Check for new start of line
|
|
||||||
None => {
|
None => {
|
||||||
if cell.flags.contains(line.flag) {
|
self.inner.insert(*flag, vec![rect]);
|
||||||
line.range = Some((cell.clone(), cell.into()));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a rectangle
|
// Add a rectangle
|
||||||
pub fn push(&mut self, rect: Rect<f32>, color: Rgb) {
|
pub fn push(&mut self, rect: Rect<f32>, color: Rgb) {
|
||||||
self.inner.push((rect, color));
|
let line = Line { start: Point::default(), color, rect };
|
||||||
|
|
||||||
|
// Flag `HIDDEN` for hashmap index is arbitrary
|
||||||
|
match self.inner.get_mut(&Flags::HIDDEN) {
|
||||||
|
Some(lines) => lines.push(line),
|
||||||
|
None => {
|
||||||
|
self.inner.insert(Flags::HIDDEN, vec![line]);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a rectangle that starts on the left of `start` and ends on the right
|
|
||||||
/// of `end`, based on the given flag and size metrics.
|
|
||||||
fn create_rect(
|
|
||||||
start: &RenderableCell,
|
|
||||||
end: Point,
|
|
||||||
flag: Flags,
|
|
||||||
metrics: &Metrics,
|
|
||||||
size: &SizeInfo,
|
|
||||||
) -> (Rect<f32>, Rgb) {
|
|
||||||
let start_x = start.column.0 as f32 * size.cell_width;
|
|
||||||
let end_x = (end.col.0 + 1) as f32 * size.cell_width;
|
|
||||||
let width = end_x - start_x;
|
|
||||||
|
|
||||||
let (position, mut height) = match flag {
|
|
||||||
Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness),
|
|
||||||
Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness),
|
|
||||||
_ => unimplemented!("Invalid flag for cell line drawing specified"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure lines are always visible
|
|
||||||
height = height.max(1.);
|
|
||||||
|
|
||||||
let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
|
|
||||||
let baseline = cell_bottom + metrics.descent;
|
|
||||||
|
|
||||||
let mut y = baseline - position - height / 2.;
|
|
||||||
let max_y = cell_bottom - height;
|
|
||||||
if y > max_y {
|
|
||||||
y = max_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rect =
|
|
||||||
Rect::new(start_x + size.padding_x, y.round() + size.padding_y, width, height.round());
|
|
||||||
|
|
||||||
(rect, start.fg)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue