summaryrefslogtreecommitdiff
path: root/src/body_layer.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/body_layer.zig')
-rw-r--r--src/body_layer.zig56
1 files changed, 32 insertions, 24 deletions
diff --git a/src/body_layer.zig b/src/body_layer.zig
index ab000795..04c84b62 100644
--- a/src/body_layer.zig
+++ b/src/body_layer.zig
@@ -569,13 +569,14 @@ pub fn paintHeaders(p: *Pardes) !void {
const global = tagline.topbar(p);
const global_rows = p.topBarHeight();
const global_scroll = if (p.header_focus and p.header_column == null) p.header_scroll else 0;
+ const global_top = if (p.header_focus and p.header_column == null) p.header_top else 0;
if (p.settings.workspace_tag) {
s.fill(0, 0, s.cols, global_rows, .{
.bg = .{ .rgb = chrome.tag_bg },
.font_role = .tagline,
});
for (0..global_rows) |row| {
- const line = modal.lineSlice(global, row);
+ const line = modal.lineSlice(global, row + global_top);
_ = s.print(0, @intCast(row), s.cols, line[panes.File.rawAtDisplay(line, global_scroll)..], .{
.fg = .{ .rgb = chrome.tag_fg },
.bg = .{ .rgb = chrome.tag_bg },
@@ -584,7 +585,7 @@ pub fn paintHeaders(p: *Pardes) !void {
}
}
if (p.pointer_inside and p.hover_row < global_rows) {
- const bar = modal.lineSlice(global, p.hover_row);
+ const bar = modal.lineSlice(global, p.hover_row + global_top);
if (wordBoundsAtCol(bar, panes.File.rawAtDisplay(bar, p.hover_col + global_scroll))) |bounds| {
var col = panes.File.rawDisplayCol(bar, bounds.lo) -| global_scroll;
const hi = panes.File.rawDisplayCol(bar, bounds.hi) -| global_scroll;
@@ -619,9 +620,10 @@ pub fn paintHeaders(p: *Pardes) !void {
s.fill(x, y0, w, column_rows, .{ .bg = .{ .rgb = bg }, .font_role = .tagline });
const bar_scroll = if (p.header_focus and p.header_column == c) p.header_scroll else 0;
+ const bar_top = if (p.header_focus and p.header_column == c) p.header_top else 0;
const tag = tagline.columnTag(p, c);
for (0..column_rows) |row| {
- const line = modal.lineSlice(tag, row);
+ const line = modal.lineSlice(tag, row + bar_top);
_ = s.print(x, y0 + @as(u16, @intCast(row)), w, line[panes.File.rawAtDisplay(line, bar_scroll)..], .{
.fg = .{ .rgb = fg },
.bg = .{ .rgb = bg },
@@ -630,7 +632,7 @@ pub fn paintHeaders(p: *Pardes) !void {
}
// A carried column's pointer is aiming at a place, not a word.
if (p.pointer_inside and p.drag != .column_move and p.hover_row >= y0 and p.hover_row < y0 + column_rows and p.hover_col >= x and p.hover_col < x + w) {
- const bar = modal.lineSlice(tag, p.hover_row - y0);
+ const bar = modal.lineSlice(tag, p.hover_row - y0 + bar_top);
if (wordBoundsAtCol(bar, panes.File.rawAtDisplay(bar, p.hover_col - x + bar_scroll))) |bounds| {
var col = panes.File.rawDisplayCol(bar, bounds.lo) -| bar_scroll;
const hi = panes.File.rawDisplayCol(bar, bounds.hi) -| bar_scroll;
@@ -649,8 +651,8 @@ pub fn paintHeaders(p: *Pardes) !void {
const y: u16 = if (p.header_column != null) global_rows else 0;
const rows = if (p.header_column != null) column_rows else global_rows;
for (0..rows) |row| {
- const bar = modal.lineSlice(text, row);
- const span = tagline.headerSelection(t, text, row) orelse continue;
+ const bar = modal.lineSlice(text, row + p.header_top);
+ const span = tagline.headerSelection(t, text, row + p.header_top) orelse continue;
var col = panes.File.rawDisplayCol(bar, span.lo) -| p.header_scroll;
const hi = panes.File.rawDisplayCol(bar, span.hi) -| p.header_scroll;
while (col < hi and col < w) : (col += 1) {
@@ -659,10 +661,11 @@ pub fn paintHeaders(p: *Pardes) !void {
cell.style.bg = .{ .rgb = th.sel_bg };
}
}
- if (t.cur_row >= 0 and t.cur_row < rows) {
- const bar = modal.lineSlice(text, @intCast(t.cur_row));
+ const cur: usize = @intCast(@max(0, t.cur_row));
+ if (cur >= p.header_top and cur < p.header_top + rows) {
+ const bar = modal.lineSlice(text, cur);
const caret = panes.File.rawDisplayCol(bar, @min(@as(usize, @intCast(@max(0, t.cur_col))), bar.len)) -| p.header_scroll;
- if (caret < w) s.cursor = .{ .x = x + @as(u16, @intCast(caret)), .y = y + @as(u16, @intCast(t.cur_row)), .bar = t.mode == .insert };
+ if (caret < w) s.cursor = .{ .x = x + @as(u16, @intCast(caret)), .y = y + @as(u16, @intCast(cur - p.header_top)), .bar = t.mode == .insert };
}
}
}
@@ -704,6 +707,7 @@ pub fn renderTagLayers(p: *Pardes, arena: std.mem.Allocator) !void {
// its viewport.
var widest: usize = 0;
var split = std.mem.splitScalar(u8, tag, '\n');
+ for (0..pane.tag_top) |_| _ = split.next();
for (0..rows) |_| widest = @max(widest, panes.File.displayWidth(split.next() orelse ""));
const columns: u16 = @intCast(@min(std.math.maxInt(u16) - TAG_TEXT_INSET, @max(p.tagCapacity(r.w - TAG_TEXT_INSET), widest + 1)));
const pitch = @as(usize, columns) + TAG_TEXT_INSET;
@@ -740,7 +744,8 @@ pub fn renderTagLayers(p: *Pardes, arena: std.mem.Allocator) !void {
if (p.settings.workspace_tag) {
for (0..p.topBarHeight()) |line| {
const index = if (line == 0) MAX_PANES + MAX_COLS else HEADER_LINE_LAYER_BASE + MAX_COLS * (MAX_TAG_ROWS - 1) + line - 1;
- try renderHeaderLayer(p, arena, index, .workspace, 0, @intCast(line), .{ .x = 0, .y = @intCast(line), .w = p.screen_w, .h = TOPBAR_H }, modal.lineSlice(tagline.topbar(p), line), chrome.tag_bg, chrome.tag_fg);
+ const top = if (p.header_focus and p.header_column == null) p.header_top else 0;
+ try renderHeaderLayer(p, arena, index, .workspace, 0, @intCast(line), .{ .x = 0, .y = @intCast(line), .w = p.screen_w, .h = TOPBAR_H }, modal.lineSlice(tagline.topbar(p), line + top), chrome.tag_bg, chrome.tag_fg);
}
}
// Each notice is its own tagline-shaped chip over the top of the body.
@@ -805,9 +810,10 @@ pub fn renderTagLayers(p: *Pardes, arena: std.mem.Allocator) !void {
if (p.columnBarHeight() > 0) for (0..p.ncol) |column| {
if (p.col_w[column] == 0) continue;
const active = if (layout.findPane(p, p.active)) |found| found.col == column and p.settings.focus_tint else false;
+ const top = if (p.header_focus and p.header_column == column) p.header_top else 0;
for (0..p.columnBarHeight()) |line| {
const index = if (line == 0) MAX_PANES + column else HEADER_LINE_LAYER_BASE + column * (MAX_TAG_ROWS - 1) + line - 1;
- try renderHeaderLayer(p, arena, index, .column, @intCast(column), @intCast(line), .{ .x = p.col_x[column] + @min(TAG_TEXT_INSET, p.col_w[column]), .y = p.topBarHeight() + @as(u16, @intCast(line)), .w = p.col_w[column] -| TAG_TEXT_INSET, .h = COLBAR_H }, modal.lineSlice(tagline.columnTag(p, column), line), if (active) chrome.tag_active_bg else chrome.tag_bg, if (active) chrome.tag_active_fg else chrome.tag_fg);
+ try renderHeaderLayer(p, arena, index, .column, @intCast(column), @intCast(line), .{ .x = p.col_x[column] + @min(TAG_TEXT_INSET, p.col_w[column]), .y = p.topBarHeight() + @as(u16, @intCast(line)), .w = p.col_w[column] -| TAG_TEXT_INSET, .h = COLBAR_H }, modal.lineSlice(tagline.columnTag(p, column), line + top), if (active) chrome.tag_active_bg else chrome.tag_bg, if (active) chrome.tag_active_fg else chrome.tag_fg);
}
};
}
@@ -846,7 +852,7 @@ fn renderHeaderLayer(p: *Pardes, arena: std.mem.Allocator, index: usize, kind: T
surface.fill(0, 0, columns, 1, .{ .bg = .{ .rgb = bg }, .font_role = .tagline });
_ = surface.print(0, 0, columns, text[panes.File.rawAtDisplay(text, scroll)..], .{ .fg = .{ .rgb = fg }, .bg = .{ .rgb = bg }, .font_role = .tagline });
if (editing) |t| {
- if (tagline.headerSelection(t, try edit.flatSurface(p, t), line)) |span| {
+ if (tagline.headerSelection(t, try edit.flatSurface(p, t), line + p.header_top)) |span| {
var col = panes.File.rawDisplayCol(text, span.lo) -| scroll;
const end = panes.File.rawDisplayCol(text, span.hi) -| scroll;
while (col < end and col < columns) : (col += 1) {
@@ -855,7 +861,7 @@ fn renderHeaderLayer(p: *Pardes, arena: std.mem.Allocator, index: usize, kind: T
}
}
const caret = panes.File.rawDisplayCol(text, @min(@as(usize, @intCast(@max(0, t.cur_col))), text.len)) -| scroll;
- if (t.cur_row == line and caret < columns) layer.cursor = .{ .col = @intCast(caret), .bar = t.mode == .insert };
+ if (t.cur_row == line + p.header_top and caret < columns) layer.cursor = .{ .col = @intCast(caret), .bar = t.mode == .insert };
} else if (p.pointer_inside) {
const tag_hit = p.pointer_tag_hit;
const col: ?usize = if (tag_hit) |pointer|
@@ -899,10 +905,12 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
const whole = try tagline.tagText(p, arena, pane);
const tail = tagline.curTail(pane);
const prefix_len = whole.len - tail.len;
- // A row per line: the first carries the prefix, the rest are the
- // tag's own text alone.
+ // A row per line shown, from `tag_top`: the tag's first line carries
+ // the prefix, the rest are the tag's own text alone.
+ const top = pane.tag_top;
var lines: [MAX_TAG_ROWS][]const u8 = @splat("");
var split = std.mem.splitScalar(u8, whole, '\n');
+ for (0..top) |_| _ = split.next();
for (lines[0..rows]) |*line| line.* = split.next() orelse "";
// The keyboard's caret, as a row and a byte offset in that row's
// line: in the tag's own text when the tag has the keyboard, in the
@@ -912,9 +920,9 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
null
else if (pane.prompt == .name)
.{ .row = 0, .at = @min(lines[0].len, @as(usize, @intCast(@max(0, pane.input.cur_col)))) }
- else if (pane.focus == .tag and pane.tag.cur_row < rows) caret: {
- const row: usize = @intCast(@max(0, pane.tag.cur_row));
- const lead: usize = if (row == 0) prefix_len else 0;
+ else if (pane.focus == .tag and pane.tag.cur_row >= top and pane.tag.cur_row < top + rows) caret: {
+ const row: usize = @intCast(pane.tag.cur_row - top);
+ const lead: usize = if (row + top == 0) prefix_len else 0;
break :caret .{ .row = row, .at = @min(lines[row].len, lead + @as(usize, @intCast(@max(0, pane.tag.cur_col)))) };
} else null;
if (caret) |at| {
@@ -936,7 +944,7 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
_ = s.print(tx, tag_y + @as(u16, @intCast(row)), tw, line[start..], tag_style);
}
const tag = lines[0];
- if (tagTintRange(pane, tag, prefix_len)) |name| {
+ if (top == 0) if (tagTintRange(pane, tag, prefix_len)) |name| {
const lo = @max(name.lo, panes.File.rawAtDisplay(tag, pane.tag_scroll));
const hi = @min(name.hi, tag.len);
if (lo < hi) {
@@ -946,7 +954,7 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
while (col < end and col < tw) : (col += 1)
s.at(tx + @as(u16, @intCast(col)), tag_y).style.fg = .{ .rgb = name_fg };
}
- }
+ };
if (p.look_hover_preview) |preview| {
if (preview.pane == id and preview.serial == pane.serial) if (preview.sel) |sel| if (@min(sel.r0, sel.r1) < rows) {
const preview_bg = colors.mix(page_rgb, colors.mix(page_rgb, th.sel_bg));
@@ -964,15 +972,15 @@ pub fn paintPaneTag(p: *Pardes, arena: std.mem.Allocator, pane: *Pane, r: Rect,
if (modal.nextGrapheme(tail, lo) >= hi) continue;
const first = modal.positionAt(tail, lo);
const last = modal.positionAt(tail, hi);
- var row = first.row;
- while (row <= last.row and row < rows) : (row += 1) {
- const line = lines[row];
+ var row = @max(first.row, top);
+ while (row <= last.row and row < top + rows) : (row += 1) {
+ const line = lines[row - top];
const lead: usize = if (row == 0) prefix_len else 0;
const from = lead + if (row == first.row) first.col else 0;
const to = @min(line.len, lead + if (row == last.row) last.col else modal.lineSlice(tail, row).len);
var col = panes.File.rawDisplayCol(line, from) -| pane.tag_scroll;
const end = panes.File.rawDisplayCol(line, to);
- const y = tag_y + @as(u16, @intCast(row));
+ const y = tag_y + @as(u16, @intCast(row - top));
while (col + pane.tag_scroll < end and col < tw) : (col += 1) {
const cell = s.at(tx + @as(u16, @intCast(col)), y);
cell.default = false;