renderer: hook up search selection match highlighting
parent
333dd08c97
commit
880db9fdd0
|
|
@ -1363,6 +1363,32 @@ fn searchCallback_(
|
|||
try self.renderer_thread.wakeup.notify();
|
||||
},
|
||||
|
||||
.selected_match => |selected_| {
|
||||
if (selected_) |sel| {
|
||||
// Copy the flattened match.
|
||||
var arena: ArenaAllocator = .init(self.alloc);
|
||||
errdefer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const match = try sel.highlight.clone(alloc);
|
||||
|
||||
_ = self.renderer_thread.mailbox.push(
|
||||
.{ .search_selected_match = .{
|
||||
.arena = arena,
|
||||
.match = match,
|
||||
} },
|
||||
.forever,
|
||||
);
|
||||
} else {
|
||||
// Reset our selected match
|
||||
_ = self.renderer_thread.mailbox.push(
|
||||
.{ .search_selected_match = null },
|
||||
.forever,
|
||||
);
|
||||
}
|
||||
|
||||
try self.renderer_thread.wakeup.notify();
|
||||
},
|
||||
|
||||
// When we quit, tell our renderer to reset any search state.
|
||||
.quit => {
|
||||
_ = self.renderer_thread.mailbox.push(
|
||||
|
|
@ -1376,7 +1402,6 @@ fn searchCallback_(
|
|||
},
|
||||
|
||||
// Unhandled, so far.
|
||||
.selected_match,
|
||||
.total_matches,
|
||||
.complete,
|
||||
=> {},
|
||||
|
|
|
|||
|
|
@ -988,10 +988,25 @@ palette: Palette = .{},
|
|||
/// - "cell-foreground" to match the cell foreground color
|
||||
/// - "cell-background" to match the cell background color
|
||||
///
|
||||
/// The default value is
|
||||
/// The default value is black text on a golden yellow background.
|
||||
@"search-foreground": TerminalColor = .{ .color = .{ .r = 0, .g = 0, .b = 0 } },
|
||||
@"search-background": TerminalColor = .{ .color = .{ .r = 0xFF, .g = 0xE0, .b = 0x82 } },
|
||||
|
||||
/// The foreground and background color for the currently selected search match.
|
||||
/// This is the focused match that will be jumped to when using next/previous
|
||||
/// search navigation.
|
||||
///
|
||||
/// Valid values:
|
||||
///
|
||||
/// - Hex (`#RRGGBB` or `RRGGBB`)
|
||||
/// - Named X11 color
|
||||
/// - "cell-foreground" to match the cell foreground color
|
||||
/// - "cell-background" to match the cell background color
|
||||
///
|
||||
/// The default value is black text on a bright orange background.
|
||||
@"search-selected-foreground": TerminalColor = .{ .color = .{ .r = 0, .g = 0, .b = 0 } },
|
||||
@"search-selected-background": TerminalColor = .{ .color = .{ .r = 0xFE, .g = 0xA6, .b = 0x2B } },
|
||||
|
||||
/// The command to run, usually a shell. If this is not an absolute path, it'll
|
||||
/// be looked up in the `PATH`. If this is not set, a default will be looked up
|
||||
/// from your system. The rules for the default lookup are:
|
||||
|
|
|
|||
|
|
@ -459,6 +459,14 @@ fn drainMailbox(self: *Thread) !void {
|
|||
self.renderer.search_matches_dirty = true;
|
||||
},
|
||||
|
||||
.search_selected_match => |v| {
|
||||
// Note we don't free the new value because we expect our
|
||||
// allocators to match.
|
||||
if (self.renderer.search_selected_match) |*m| m.arena.deinit();
|
||||
self.renderer.search_selected_match = v;
|
||||
self.renderer.search_matches_dirty = true;
|
||||
},
|
||||
|
||||
.inspector => |v| self.flags.has_inspector = v,
|
||||
|
||||
.macos_display_id => |v| {
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
/// Note that the selections MAY BE INVALID (point to PageList nodes
|
||||
/// that do not exist anymore). These must be validated prior to use.
|
||||
search_matches: ?renderer.Message.SearchMatches,
|
||||
search_selected_match: ?renderer.Message.SearchMatch,
|
||||
search_matches_dirty: bool,
|
||||
|
||||
/// The current set of cells to render. This is rebuilt on every frame
|
||||
|
|
@ -222,6 +223,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
/// a large screen.
|
||||
terminal_state_frame_count: usize = 0,
|
||||
|
||||
const HighlightTag = enum(u8) {
|
||||
search_match,
|
||||
search_match_selected,
|
||||
};
|
||||
|
||||
/// Swap chain which maintains multiple copies of the state needed to
|
||||
/// render a frame, so that we can start building the next frame while
|
||||
/// the previous frame is still being processed on the GPU.
|
||||
|
|
@ -539,6 +545,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
selection_foreground: ?configpkg.Config.TerminalColor,
|
||||
search_background: configpkg.Config.TerminalColor,
|
||||
search_foreground: configpkg.Config.TerminalColor,
|
||||
search_selected_background: configpkg.Config.TerminalColor,
|
||||
search_selected_foreground: configpkg.Config.TerminalColor,
|
||||
bold_color: ?configpkg.BoldColor,
|
||||
faint_opacity: u8,
|
||||
min_contrast: f32,
|
||||
|
|
@ -612,6 +620,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
.selection_foreground = config.@"selection-foreground",
|
||||
.search_background = config.@"search-background",
|
||||
.search_foreground = config.@"search-foreground",
|
||||
.search_selected_background = config.@"search-selected-background",
|
||||
.search_selected_foreground = config.@"search-selected-foreground",
|
||||
|
||||
.custom_shaders = custom_shaders,
|
||||
.bg_image = bg_image,
|
||||
|
|
@ -687,6 +697,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
.scrollbar = .zero,
|
||||
.scrollbar_dirty = false,
|
||||
.search_matches = null,
|
||||
.search_selected_match = null,
|
||||
.search_matches_dirty = false,
|
||||
|
||||
// Render state
|
||||
|
|
@ -760,6 +771,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.terminal_state.deinit(self.alloc);
|
||||
if (self.search_selected_match) |*m| m.arena.deinit();
|
||||
if (self.search_matches) |*m| m.arena.deinit();
|
||||
self.swap_chain.deinit();
|
||||
|
||||
|
|
@ -1209,9 +1221,24 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
highlights.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
// NOTE: The order below matters. Highlights added earlier
|
||||
// will take priority.
|
||||
|
||||
if (self.search_selected_match) |m| {
|
||||
self.terminal_state.updateHighlightsFlattened(
|
||||
self.alloc,
|
||||
@intFromEnum(HighlightTag.search_match_selected),
|
||||
(&m.match)[0..1],
|
||||
) catch |err| {
|
||||
// Not a critical error, we just won't show highlights.
|
||||
log.warn("error updating search selected highlight err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
if (self.search_matches) |m| {
|
||||
self.terminal_state.updateHighlightsFlattened(
|
||||
self.alloc,
|
||||
@intFromEnum(HighlightTag.search_match),
|
||||
m.matches,
|
||||
) catch |err| {
|
||||
// Not a critical error, we just won't show highlights.
|
||||
|
|
@ -2560,13 +2587,18 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
false,
|
||||
selection,
|
||||
search,
|
||||
search_selected,
|
||||
} = selected: {
|
||||
// If we're highlighted, then we're selected. In the
|
||||
// future we want to use a different style for this
|
||||
// but this to get started.
|
||||
for (highlights.items) |hl| {
|
||||
if (x >= hl[0] and x <= hl[1]) {
|
||||
break :selected .search;
|
||||
if (x >= hl.range[0] and x <= hl.range[1]) {
|
||||
const tag: HighlightTag = @enumFromInt(hl.tag);
|
||||
break :selected switch (tag) {
|
||||
.search_match => .search,
|
||||
.search_match_selected => .search_selected,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2614,6 +2646,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
.@"cell-background" => if (style.flags.inverse) fg_style else bg_style,
|
||||
},
|
||||
|
||||
.search_selected => switch (self.config.search_selected_background) {
|
||||
.color => |color| color.toTerminalRGB(),
|
||||
.@"cell-foreground" => if (style.flags.inverse) bg_style else fg_style,
|
||||
.@"cell-background" => if (style.flags.inverse) fg_style else bg_style,
|
||||
},
|
||||
|
||||
// Not selected
|
||||
.false => if (style.flags.inverse != isCovering(cell.codepoint()))
|
||||
// Two cases cause us to invert (use the fg color as the bg)
|
||||
|
|
@ -2652,6 +2690,12 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||
.@"cell-background" => if (style.flags.inverse) fg_style else final_bg,
|
||||
},
|
||||
|
||||
.search_selected => switch (self.config.search_selected_foreground) {
|
||||
.color => |color| color.toTerminalRGB(),
|
||||
.@"cell-foreground" => if (style.flags.inverse) final_bg else fg_style,
|
||||
.@"cell-background" => if (style.flags.inverse) fg_style else final_bg,
|
||||
},
|
||||
|
||||
.false => if (style.flags.inverse)
|
||||
final_bg
|
||||
else
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ pub const Message = union(enum) {
|
|||
/// viewport. The renderer must handle this gracefully.
|
||||
search_viewport_matches: SearchMatches,
|
||||
|
||||
/// The selected match from the search thread. May be null to indicate
|
||||
/// no match currently.
|
||||
search_selected_match: ?SearchMatch,
|
||||
|
||||
/// Activate or deactivate the inspector.
|
||||
inspector: bool,
|
||||
|
||||
|
|
@ -69,6 +73,11 @@ pub const Message = union(enum) {
|
|||
matches: []const terminal.highlight.Flattened,
|
||||
};
|
||||
|
||||
pub const SearchMatch = struct {
|
||||
arena: ArenaAllocator,
|
||||
match: terminal.highlight.Flattened,
|
||||
};
|
||||
|
||||
/// Initialize a change_config message.
|
||||
pub fn initChangeConfig(alloc: Allocator, config: *const configpkg.Config) !Message {
|
||||
const thread_ptr = try alloc.create(renderer.Thread.DerivedConfig);
|
||||
|
|
|
|||
|
|
@ -193,9 +193,17 @@ pub const RenderState = struct {
|
|||
/// The x range of the selection within this row.
|
||||
selection: ?[2]size.CellCountInt,
|
||||
|
||||
/// The x ranges of highlights within this row. Highlights are
|
||||
/// applied after the update by calling `updateHighlights`.
|
||||
highlights: std.ArrayList([2]size.CellCountInt),
|
||||
/// The highlights within this row.
|
||||
highlights: std.ArrayList(Highlight),
|
||||
};
|
||||
|
||||
pub const Highlight = struct {
|
||||
/// A special tag that can be used by the caller to differentiate
|
||||
/// different highlight types. The value is opaque to the RenderState.
|
||||
tag: u8,
|
||||
|
||||
/// The x ranges of highlights within this row.
|
||||
range: [2]size.CellCountInt,
|
||||
};
|
||||
|
||||
pub const Cell = struct {
|
||||
|
|
@ -646,6 +654,7 @@ pub const RenderState = struct {
|
|||
pub fn updateHighlightsFlattened(
|
||||
self: *RenderState,
|
||||
alloc: Allocator,
|
||||
tag: u8,
|
||||
hls: []const highlight.Flattened,
|
||||
) Allocator.Error!void {
|
||||
// Fast path, we have no highlights!
|
||||
|
|
@ -691,8 +700,11 @@ pub const RenderState = struct {
|
|||
try row_highlights.append(
|
||||
arena_alloc,
|
||||
.{
|
||||
if (i == 0) hl.top_x else 0,
|
||||
if (i == nodes.len - 1) hl.bot_x else self.cols - 1,
|
||||
.tag = tag,
|
||||
.range = .{
|
||||
if (i == 0) hl.top_x else 0,
|
||||
if (i == nodes.len - 1) hl.bot_x else self.cols - 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue