diff options
Diffstat (limited to 'src/gui')
| -rw-r--r-- | src/gui/gui.zig | 116 |
1 files changed, 87 insertions, 29 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 7a9c2135..a03e51ce 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -153,7 +153,7 @@ test "GUI animation clock is active-only and cadence gated" { /// Web tap-vs-scroll heuristic, measured after normalized coordinates have /// been mapped into backing pixels (12px = 8 CSS px at web_render_scale). const touch_tap_slop_px: f32 = 12.0; -const max_overlay_vertices: usize = touch_click_flash_vertices + max_touch_points * (1100 + max_touch_trail_points * overlay_circle_vertices); +const max_overlay_vertices: usize = 6 + touch_click_flash_vertices + max_touch_points * (1100 + max_touch_trail_points * overlay_circle_vertices); // Ctrl+ / Ctrl-: how far one press moves g.px, and the two sizes it stops at. // ONE size for the whole window, not one per pane: the core lays every pane @@ -3230,7 +3230,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard var col = x0; while (col < x0 + bw) : (col += 1) { const sidx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, surface.at(col, row), sidx == cursor_idx, cursor_bar); + emitInstance(g, instances, base + n, col, row, shifted, win_w, win_h, surface.at(col, row), sidx == cursor_idx and !cursor_bar); n += 1; } } @@ -3240,7 +3240,7 @@ fn emitScrollRows(g: *Gui, instances: [*]CellInstance, base: u32, surface: *pard const erow: u16 = if (g.scroll_lag > 0) y0 + bh else y0 - 1; var i: u16 = 0; while (i < bw) : (i += 1) { - emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, &g.scroll_edge[i], false, false); + emitInstance(g, instances, base + n, x0 + i, erow, shifted, win_w, win_h, &g.scroll_edge[i], false); n += 1; } } @@ -3579,7 +3579,8 @@ fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on if (crt_on) try ensureSceneTexture(g, sw, sh); const scene: *c.SDL_GPUTexture = if (crt_on) g.scene_tex.? else target; - const overlay_count = buildTouchOverlay(g, sw, sh, debug_on); + const layout = fixedCellLayout(g); + const overlay_count = buildOverlay(g, surface, layout, sw, sh, debug_on); if (overlay_count != 0) uploadOverlayGpu(g, cmd, overlay_count); const image_count = prepareNativeImages(g, gpa, cmd, surface, sw, sh); @@ -3610,7 +3611,6 @@ fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on return; }); const instances: [*]CellInstance = @ptrCast(@alignCast(vptr)); - const layout = fixedCellLayout(g); const cursor_idx: u32 = if (surface.cursor) |cu| @as(u32, cu.y) * surface.cols + cu.x else cells; const cursor_bar = if (surface.cursor) |cu| cu.bar else false; var row: u16 = 0; @@ -3618,7 +3618,7 @@ fn renderFrame(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, crt_on var col: u16 = 0; while (col < surface.cols) : (col += 1) { const idx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, instances, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx, cursor_bar); + emitInstance(g, instances, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx and !cursor_bar); } } const shifted = emitScrollRows(g, instances, cells, surface, layout, win_w, win_h); @@ -3698,15 +3698,13 @@ fn emitInstance( win_h: f32, cell: *const pardes.Cell, is_cursor: bool, - cursor_bar: bool, ) void { - // resolve the cell style to concrete fg/bg, matching the prototype's - // cellKey: reverse swaps, invisible hides, dim darkens. The cursor cell - // renders as reverse (cancelling an already-reversed cell); an insert - // cursor keeps the cell colors and draws a bar glyph instead. + // Resolve the cell style to concrete fg/bg. A normal block cursor reverses + // the cell; the insert cursor is a separate overlay, so the glyph beneath + // it remains in this base pass. var fg = fg_default; var bg = bg_default; - var reverse = is_cursor and !cursor_bar; + var reverse = is_cursor; if (!cell.default) { const st = cell.style; fg = switch (st.fg) { @@ -3735,11 +3733,7 @@ fn emitInstance( const y0 = 1.0 - (py0 / win_h) * 2.0; const y1 = 1.0 - ((py0 + layout.h) / win_h) * 2.0; - const grapheme = cell.grapheme(); - var cp: u32 = if (cell.default or grapheme.len == 0) ' ' else firstCp(grapheme); - // ponytail: the insert bar is the left-eighth block glyph over the cell - // (hides the char under it); a dedicated quad if that ever matters - if (is_cursor and cursor_bar) cp = 0x258F; + const cp = cellCodepoint(cell); const slot = if (cp == ' ') g.space_slot else ensureGlyph(g, cp); const aw_f: f32 = @floatFromInt(atlas_w); const ah_f: f32 = @floatFromInt(atlas_h); @@ -3762,6 +3756,34 @@ fn emitInstance( }; } +fn cellCodepoint(cell: *const pardes.Cell) u32 { + const grapheme = cell.grapheme(); + return if (cell.default or grapheme.len == 0) ' ' else firstCp(grapheme); +} + +test "insert cursor overlays without replacing the character beneath it" { + const cell: pardes.Cell = .{ + .text = .{ 'x', ' ', ' ', ' ', ' ', ' ', ' ' }, + .len = 1, + .default = false, + }; + try std.testing.expectEqual(@as(u32, 'x'), cellCodepoint(&cell)); + + var vertices: [6]OverlayVertex = undefined; + var builder: OverlayBuilder = .{ + .vertices = &vertices, + .win_w = 100, + .win_h = 100, + }; + addCursorBar(&builder, 2, 3, true, .{ + .x_off = 0, + .y_off = 0, + .w = 10, + .h = 20, + }); + try std.testing.expectEqual(@as(usize, 6), builder.len); +} + fn palColor(idx: u8) [3]u8 { const p = ghostty_vt.color.default[idx]; return .{ p.r, p.g, p.b }; @@ -4058,9 +4080,9 @@ fn renderFrameGl(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, debu ); c.glClear(c.GL_COLOR_BUFFER_BIT); - const overlay_count = buildTouchOverlay(g, sw, sh, debug_on); const cells: u32 = @as(u32, surface.cols) * surface.rows; if (cells == 0) { + const overlay_count = buildTouchOverlay(g, sw, sh, debug_on); drawOverlayGl(g, overlay_count); _ = c.SDL_GL_SwapWindow(g.window); return; @@ -4074,6 +4096,7 @@ fn renderFrameGl(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, debu const win_w: f32 = @floatFromInt(sw); const win_h: f32 = @floatFromInt(sh); const layout = cellLayout(g, surface.cols, surface.rows, sw, sh); + const overlay_count = buildOverlay(g, surface, layout, sw, sh, debug_on); const cursor_idx: u32 = if (surface.cursor) |cu| @as(u32, cu.y) * surface.cols + cu.x else cells; const cursor_bar = if (surface.cursor) |cu| cu.bar else false; var row: u16 = 0; @@ -4081,7 +4104,7 @@ fn renderFrameGl(g: *Gui, gpa: std.mem.Allocator, surface: *pardes.Surface, debu var col: u16 = 0; while (col < surface.cols) : (col += 1) { const idx: u32 = @as(u32, row) * surface.cols + col; - emitInstance(g, g.gl_instances.ptr, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx, cursor_bar); + emitInstance(g, g.gl_instances.ptr, idx, col, row, layout, win_w, win_h, surface.at(col, row), idx == cursor_idx and !cursor_bar); } } const shifted = emitScrollRows(g, g.gl_instances.ptr, cells, surface, layout, win_w, win_h); @@ -4305,15 +4328,51 @@ fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u // flash HUD, alpha-blended over the grid only while the Debug builtin is on. // ===================================================================== -fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { - if (!debug_on) { - g.touch.click_flash_frames = 0; - return 0; +fn addCursorBar(builder: *OverlayBuilder, x: u16, y: u16, bar: bool, layout: CellLayout) void { + if (!bar) return; + const x0 = layout.x_off + @as(f32, @floatFromInt(x)) * layout.w; + const y0 = layout.y_off + @as(f32, @floatFromInt(y)) * layout.h; + builder.addRect( + x0, + y0, + x0 + @max(1.0, layout.w / 8.0), + y0 + layout.h, + .{ + .r = @as(f32, @floatFromInt(fg_default[0])) / 255.0, + .g = @as(f32, @floatFromInt(fg_default[1])) / 255.0, + .b = @as(f32, @floatFromInt(fg_default[2])) / 255.0, + .a = 1.0, + }, + ); +} + +fn buildOverlay(g: *Gui, surface: *const pardes.Surface, layout: CellLayout, sw: u32, sh: u32, debug_on: bool) u32 { + if (g.overlay_vertices.len == 0 or sw == 0 or sh == 0) return 0; + const win_w: f32 = @floatFromInt(sw); + const win_h: f32 = @floatFromInt(sh); + var builder = OverlayBuilder{ .vertices = g.overlay_vertices, .win_w = win_w, .win_h = win_h }; + if (surface.cursor) |cursor| { + if (cursor.x < surface.cols and cursor.y < surface.rows) + addCursorBar(&builder, cursor.x, cursor.y, cursor.bar, layout); } + appendTouchOverlay(g, &builder, debug_on); + return @intCast(builder.len); +} + +fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { if (g.overlay_vertices.len == 0 or sw == 0 or sh == 0) return 0; const win_w: f32 = @floatFromInt(sw); const win_h: f32 = @floatFromInt(sh); var builder = OverlayBuilder{ .vertices = g.overlay_vertices, .win_w = win_w, .win_h = win_h }; + appendTouchOverlay(g, &builder, debug_on); + return @intCast(builder.len); +} + +fn appendTouchOverlay(g: *Gui, builder: *OverlayBuilder, debug_on: bool) void { + if (!debug_on) { + g.touch.click_flash_frames = 0; + return; + } const t = &g.touch; if (t.click_flash_frames != 0) { @@ -4325,7 +4384,7 @@ fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { const stripe_w = 4.0; const label_w = pad * 2.0 + stripe_w + 3.0 + @as(f32, @floatFromInt(label.len)) * char_step - glyph_scale; const label_h = pad * 2.0 + 5.0 * glyph_scale; - builder.addRect(0.0, 0.0, win_w, win_h, .{ .r = 1.0, .g = 0.78, .b = 0.18, .a = 0.055 * flash }); + builder.addRect(0.0, 0.0, builder.win_w, builder.win_h, .{ .r = 1.0, .g = 0.78, .b = 0.18, .a = 0.055 * flash }); builder.addRect(8.0, 8.0, 8.0 + label_w, 8.0 + label_h, .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.54 + 0.22 * flash }); builder.addRect(8.0 + pad, 8.0 + pad, 8.0 + pad + stripe_w, 8.0 + label_h - pad, .{ .r = 1.0, .g = 0.78, .b = 0.18, .a = 0.96 }); builder.addMiniText(label[0..], 8.0 + pad + stripe_w + 3.0, 8.0 + pad, glyph_scale, .{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 0.96 }); @@ -4334,8 +4393,8 @@ fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { for (&t.points) |*tp| { if (!tp.active) continue; - const cx = std.math.clamp(tp.x, 0.0, 1.0) * win_w; - const cy = std.math.clamp(tp.y, 0.0, 1.0) * win_h; + const cx = std.math.clamp(tp.x, 0.0, 1.0) * builder.win_w; + const cy = std.math.clamp(tp.y, 0.0, 1.0) * builder.win_h; const radius = (12.0 + std.math.clamp(tp.pressure, 0.0, 1.0) * 4.0) * 4.0; const color = touchColor(tp.id); @@ -4346,8 +4405,8 @@ fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { const fade = @as(f32, @floatFromInt(trail_i + 1)) / @as(f32, @floatFromInt(tp.trail_len)); var trail_color = color; trail_color.a *= 0.42 * fade; - const tx = std.math.clamp(sample.x, 0.0, 1.0) * win_w; - const ty = std.math.clamp(sample.y, 0.0, 1.0) * win_h; + const tx = std.math.clamp(sample.x, 0.0, 1.0) * builder.win_w; + const ty = std.math.clamp(sample.y, 0.0, 1.0) * builder.win_h; const trail_radius = (12.0 + std.math.clamp(sample.pressure, 0.0, 1.0) * 4.0) * 2.8; builder.addCircle(tx, ty, trail_radius, trail_color); } @@ -4358,7 +4417,6 @@ fn buildTouchOverlay(g: *Gui, sw: u32, sh: u32, debug_on: bool) u32 { builder.addCircle(cx, cy, 2.4, .{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 0.92 }); builder.addIdTag(tp.id, cx, cy, color); } - return @intCast(builder.len); } fn uploadOverlayGpu(g: *Gui, cmd: *c.SDL_GPUCommandBuffer, vertex_count: u32) void { |
