Compare commits
No commits in common. "59d102315d52e85c7df035d5784a0256db97fa4c" and "fdda6ba1a492859425fe5577d3b159bfd4c845b0" have entirely different histories.
59d102315d
...
fdda6ba1a4
4 changed files with 20 additions and 155 deletions
|
|
@ -60,9 +60,6 @@ pub const Context = struct {
|
||||||
/// Draw commands for current frame
|
/// Draw commands for current frame
|
||||||
commands: std.ArrayListUnmanaged(Command.DrawCommand),
|
commands: std.ArrayListUnmanaged(Command.DrawCommand),
|
||||||
|
|
||||||
/// Overlay commands (drawn AFTER all regular commands - for dropdowns, tooltips, popups)
|
|
||||||
overlay_commands: std.ArrayListUnmanaged(Command.DrawCommand),
|
|
||||||
|
|
||||||
/// Input state
|
/// Input state
|
||||||
input: Input.InputState,
|
input: Input.InputState,
|
||||||
|
|
||||||
|
|
@ -139,7 +136,6 @@ pub const Context = struct {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.frame_arena = try FrameArena.init(allocator),
|
.frame_arena = try FrameArena.init(allocator),
|
||||||
.commands = .{},
|
.commands = .{},
|
||||||
.overlay_commands = .{},
|
|
||||||
.input = Input.InputState.init(),
|
.input = Input.InputState.init(),
|
||||||
.layout = Layout.LayoutState.init(width, height),
|
.layout = Layout.LayoutState.init(width, height),
|
||||||
.id_stack = .{},
|
.id_stack = .{},
|
||||||
|
|
@ -159,7 +155,6 @@ pub const Context = struct {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.frame_arena = try FrameArena.initWithSize(allocator, arena_size),
|
.frame_arena = try FrameArena.initWithSize(allocator, arena_size),
|
||||||
.commands = .{},
|
.commands = .{},
|
||||||
.overlay_commands = .{},
|
|
||||||
.input = Input.InputState.init(),
|
.input = Input.InputState.init(),
|
||||||
.layout = Layout.LayoutState.init(width, height),
|
.layout = Layout.LayoutState.init(width, height),
|
||||||
.id_stack = .{},
|
.id_stack = .{},
|
||||||
|
|
@ -176,7 +171,6 @@ pub const Context = struct {
|
||||||
/// Clean up resources
|
/// Clean up resources
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.commands.deinit(self.allocator);
|
self.commands.deinit(self.allocator);
|
||||||
self.overlay_commands.deinit(self.allocator);
|
|
||||||
self.id_stack.deinit(self.allocator);
|
self.id_stack.deinit(self.allocator);
|
||||||
self.dirty_rects.deinit(self.allocator);
|
self.dirty_rects.deinit(self.allocator);
|
||||||
self.frame_arena.deinit();
|
self.frame_arena.deinit();
|
||||||
|
|
@ -189,7 +183,6 @@ pub const Context = struct {
|
||||||
|
|
||||||
// Reset per-frame state
|
// Reset per-frame state
|
||||||
self.commands.clearRetainingCapacity();
|
self.commands.clearRetainingCapacity();
|
||||||
self.overlay_commands.clearRetainingCapacity();
|
|
||||||
self.id_stack.clearRetainingCapacity();
|
self.id_stack.clearRetainingCapacity();
|
||||||
self.dirty_rects.clearRetainingCapacity();
|
self.dirty_rects.clearRetainingCapacity();
|
||||||
self.layout.reset(self.width, self.height);
|
self.layout.reset(self.width, self.height);
|
||||||
|
|
@ -219,8 +212,8 @@ pub const Context = struct {
|
||||||
|
|
||||||
self.input.endFrame();
|
self.input.endFrame();
|
||||||
|
|
||||||
// Update final stats (includes overlay commands)
|
// Update final stats
|
||||||
self.stats.command_count = self.commands.items.len + self.overlay_commands.items.len;
|
self.stats.command_count = self.commands.items.len;
|
||||||
self.stats.arena_bytes = self.frame_arena.bytesUsed();
|
self.stats.arena_bytes = self.frame_arena.bytesUsed();
|
||||||
self.stats.dirty_rect_count = self.dirty_rects.items.len;
|
self.stats.dirty_rect_count = self.dirty_rects.items.len;
|
||||||
|
|
||||||
|
|
@ -402,12 +395,6 @@ pub const Context = struct {
|
||||||
self.commands.append(self.allocator, cmd) catch {};
|
self.commands.append(self.allocator, cmd) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push an overlay command (drawn AFTER all regular commands)
|
|
||||||
/// Use this for dropdowns, tooltips, popups that need to appear on top
|
|
||||||
pub fn pushOverlayCommand(self: *Self, cmd: Command.DrawCommand) void {
|
|
||||||
self.overlay_commands.append(self.allocator, cmd) catch {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize the context
|
/// Resize the context
|
||||||
pub fn resize(self: *Self, width: u32, height: u32) void {
|
pub fn resize(self: *Self, width: u32, height: u32) void {
|
||||||
self.width = width;
|
self.width = width;
|
||||||
|
|
|
||||||
|
|
@ -214,11 +214,6 @@ pub const MainLoop = struct {
|
||||||
// Execute draw commands
|
// Execute draw commands
|
||||||
self.renderer.executeAll(self.ctx.commands.items);
|
self.renderer.executeAll(self.ctx.commands.items);
|
||||||
|
|
||||||
// Execute overlay commands (dropdowns, tooltips, popups - drawn on top)
|
|
||||||
if (self.ctx.overlay_commands.items.len > 0) {
|
|
||||||
self.renderer.executeAll(self.ctx.overlay_commands.items);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ctx.endFrame();
|
self.ctx.endFrame();
|
||||||
|
|
||||||
// Present to backend
|
// Present to backend
|
||||||
|
|
@ -227,10 +222,9 @@ pub const MainLoop = struct {
|
||||||
if (self.config.debug_timing) {
|
if (self.config.debug_timing) {
|
||||||
const elapsed_ns = std.time.nanoTimestamp() - start_time;
|
const elapsed_ns = std.time.nanoTimestamp() - start_time;
|
||||||
const elapsed_ms = @as(f64, @floatFromInt(elapsed_ns)) / 1_000_000.0;
|
const elapsed_ms = @as(f64, @floatFromInt(elapsed_ns)) / 1_000_000.0;
|
||||||
const total_cmds = self.ctx.commands.items.len + self.ctx.overlay_commands.items.len;
|
|
||||||
std.debug.print("Frame {}: {} cmds, {d:.1}ms\n", .{
|
std.debug.print("Frame {}: {} cmds, {d:.1}ms\n", .{
|
||||||
self.frame_count,
|
self.frame_count,
|
||||||
total_cmds,
|
self.ctx.commands.items.len,
|
||||||
elapsed_ms,
|
elapsed_ms,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,22 +158,6 @@ pub const CellValue = union(enum) {
|
||||||
.boolean => |b| if (b) "Yes" else "No",
|
.boolean => |b| if (b) "Yes" else "No",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone value with deep copy of strings
|
|
||||||
pub fn clone(self: CellValue, allocator: std.mem.Allocator) !CellValue {
|
|
||||||
return switch (self) {
|
|
||||||
.string => |s| .{ .string = if (s.len > 0) try allocator.dupe(u8, s) else "" },
|
|
||||||
else => self, // Other types are value types, no allocation needed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free allocated string (only call for cloned values)
|
|
||||||
pub fn deinit(self: CellValue, allocator: std.mem.Allocator) void {
|
|
||||||
switch (self) {
|
|
||||||
.string => |s| if (s.len > 0) allocator.free(s),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -468,26 +452,15 @@ pub const OnActiveRowChangedFn = *const fn (old_row: ?usize, new_row: usize, row
|
||||||
pub const Row = struct {
|
pub const Row = struct {
|
||||||
data: std.StringHashMap(CellValue),
|
data: std.StringHashMap(CellValue),
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
/// True if this Row owns the string data (was created via clone)
|
|
||||||
/// When true, deinit() will free the strings
|
|
||||||
owns_data: bool = false,
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) Row {
|
pub fn init(allocator: std.mem.Allocator) Row {
|
||||||
return .{
|
return .{
|
||||||
.data = std.StringHashMap(CellValue).init(allocator),
|
.data = std.StringHashMap(CellValue).init(allocator),
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.owns_data = false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Row) void {
|
pub fn deinit(self: *Row) void {
|
||||||
// Free cloned string values if we own them
|
|
||||||
if (self.owns_data) {
|
|
||||||
var iter = self.data.iterator();
|
|
||||||
while (iter.next()) |entry| {
|
|
||||||
entry.value_ptr.*.deinit(self.allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.data.deinit();
|
self.data.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,22 +472,11 @@ pub const Row = struct {
|
||||||
try self.data.put(column, value);
|
try self.data.put(column, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deep clone: duplicates all string values
|
|
||||||
/// The cloned Row owns its data and will free it on deinit()
|
|
||||||
pub fn clone(self: *const Row, allocator: std.mem.Allocator) !Row {
|
pub fn clone(self: *const Row, allocator: std.mem.Allocator) !Row {
|
||||||
var new_row = Row{
|
var new_row = Row.init(allocator);
|
||||||
.data = std.StringHashMap(CellValue).init(allocator),
|
|
||||||
.allocator = allocator,
|
|
||||||
.owns_data = true, // Cloned row owns its string data
|
|
||||||
};
|
|
||||||
errdefer new_row.deinit();
|
|
||||||
|
|
||||||
var iter = self.data.iterator();
|
var iter = self.data.iterator();
|
||||||
while (iter.next()) |entry| {
|
while (iter.next()) |entry| {
|
||||||
// Keys are typically string literals, no need to clone
|
try new_row.data.put(entry.key_ptr.*, entry.value_ptr.*);
|
||||||
// Values need deep clone for strings
|
|
||||||
const cloned_value = try entry.value_ptr.*.clone(allocator);
|
|
||||||
try new_row.data.put(entry.key_ptr.*, cloned_value);
|
|
||||||
}
|
}
|
||||||
return new_row;
|
return new_row;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,6 @@ pub const AutoCompleteState = struct {
|
||||||
was_focused: bool = false,
|
was_focused: bool = false,
|
||||||
/// First frame flag to avoid false focus detection
|
/// First frame flag to avoid false focus detection
|
||||||
first_frame: bool = true,
|
first_frame: bool = true,
|
||||||
/// Texto original guardado al abrir dropdown (para restaurar con Escape)
|
|
||||||
saved_text: [256]u8 = [_]u8{0} ** 256,
|
|
||||||
saved_text_len: usize = 0,
|
|
||||||
saved_cursor: usize = 0,
|
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
|
@ -168,29 +164,8 @@ pub const AutoCompleteState = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Guarda el texto actual (para restaurar con Escape)
|
/// Open the dropdown
|
||||||
pub fn saveText(self: *Self) void {
|
|
||||||
@memcpy(self.saved_text[0..self.len], self.buffer[0..self.len]);
|
|
||||||
self.saved_text_len = self.len;
|
|
||||||
self.saved_cursor = self.cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Restaura el texto guardado
|
|
||||||
pub fn restoreText(self: *Self) void {
|
|
||||||
@memcpy(self.buffer[0..self.saved_text_len], self.saved_text[0..self.saved_text_len]);
|
|
||||||
self.len = self.saved_text_len;
|
|
||||||
self.cursor = self.saved_cursor;
|
|
||||||
// Sync filter
|
|
||||||
@memcpy(self.last_filter[0..self.saved_text_len], self.saved_text[0..self.saved_text_len]);
|
|
||||||
self.last_filter_len = self.saved_text_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open the dropdown (guarda texto para posible restauración con Escape)
|
|
||||||
pub fn openDropdown(self: *Self) void {
|
pub fn openDropdown(self: *Self) void {
|
||||||
if (!self.open) {
|
|
||||||
// Solo guardar si estamos abriendo (no si ya está abierto)
|
|
||||||
self.saveText();
|
|
||||||
}
|
|
||||||
self.open = true;
|
self.open = true;
|
||||||
self.highlighted = if (self.selected >= 0) self.selected else 0;
|
self.highlighted = if (self.selected >= 0) self.selected else 0;
|
||||||
}
|
}
|
||||||
|
|
@ -200,12 +175,6 @@ pub const AutoCompleteState = struct {
|
||||||
self.open = false;
|
self.open = false;
|
||||||
self.highlighted = -1;
|
self.highlighted = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close dropdown y restaurar texto original (para Escape)
|
|
||||||
pub fn closeDropdownAndRestore(self: *Self) void {
|
|
||||||
self.restoreText();
|
|
||||||
self.closeDropdown();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -328,26 +297,10 @@ pub fn autocompleteRect(
|
||||||
const input_hovered = bounds.contains(mouse.x, mouse.y);
|
const input_hovered = bounds.contains(mouse.x, mouse.y);
|
||||||
const input_clicked = input_hovered and ctx.input.mousePressed(.left);
|
const input_clicked = input_hovered and ctx.input.mousePressed(.left);
|
||||||
|
|
||||||
// Calcular área de la flecha para detección de clicks
|
|
||||||
const arrow_click_width: u32 = 20; // Zona clicable de la flecha
|
|
||||||
const arrow_area_x = bounds.x + @as(i32, @intCast(bounds.w -| arrow_click_width));
|
|
||||||
const arrow_hovered = mouse.x >= arrow_area_x and mouse.x <= bounds.x + @as(i32, @intCast(bounds.w)) and
|
|
||||||
mouse.y >= bounds.y and mouse.y <= bounds.y + @as(i32, @intCast(bounds.h));
|
|
||||||
const arrow_clicked = arrow_hovered and ctx.input.mousePressed(.left);
|
|
||||||
|
|
||||||
// Handle click to request focus
|
// Handle click to request focus
|
||||||
if (input_clicked and !config.disabled) {
|
if (input_clicked and !config.disabled) {
|
||||||
ctx.requestFocus(widget_id);
|
ctx.requestFocus(widget_id);
|
||||||
|
if (config.show_on_focus) {
|
||||||
// Click en la flecha: toggle dropdown (forzar abrir/cerrar)
|
|
||||||
if (arrow_clicked) {
|
|
||||||
if (state.open) {
|
|
||||||
state.closeDropdown();
|
|
||||||
} else {
|
|
||||||
state.openDropdown();
|
|
||||||
}
|
|
||||||
} else if (config.show_on_focus) {
|
|
||||||
// Click en el área de texto: abrir si show_on_focus está activo
|
|
||||||
state.openDropdown();
|
state.openDropdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -491,10 +444,7 @@ pub fn autocompleteRect(
|
||||||
|
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
.escape => {
|
.escape => {
|
||||||
// Escape: Cierra dropdown y RESTAURA texto original
|
state.closeDropdown();
|
||||||
if (state.open) {
|
|
||||||
state.closeDropdownAndRestore();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.enter => {
|
.enter => {
|
||||||
if (state.open and state.highlighted >= 0 and state.highlighted < @as(i32, @intCast(filtered_count))) {
|
if (state.open and state.highlighted >= 0 and state.highlighted < @as(i32, @intCast(filtered_count))) {
|
||||||
|
|
@ -514,13 +464,7 @@ pub fn autocompleteRect(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.up => {
|
.up => {
|
||||||
// Ctrl+Up: Cierra dropdown (sin restaurar - confirma el estado actual)
|
if (state.open) {
|
||||||
if (ctx.input.modifiers.ctrl) {
|
|
||||||
if (state.open) {
|
|
||||||
state.closeDropdown();
|
|
||||||
}
|
|
||||||
} else if (state.open) {
|
|
||||||
// Navegar hacia arriba en la lista
|
|
||||||
if (state.highlighted > 0) {
|
if (state.highlighted > 0) {
|
||||||
state.highlighted -= 1;
|
state.highlighted -= 1;
|
||||||
// Scroll if needed
|
// Scroll if needed
|
||||||
|
|
@ -529,18 +473,11 @@ pub fn autocompleteRect(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Abrir dropdown si está cerrado
|
|
||||||
state.openDropdown();
|
state.openDropdown();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.down => {
|
.down => {
|
||||||
// Ctrl+Down: Forzar apertura dropdown (SIN limpiar texto)
|
if (state.open) {
|
||||||
if (ctx.input.modifiers.ctrl) {
|
|
||||||
state.highlighted = 0;
|
|
||||||
state.scroll_offset = 0;
|
|
||||||
state.openDropdown();
|
|
||||||
} else if (state.open) {
|
|
||||||
// Navegar hacia abajo en la lista
|
|
||||||
if (state.highlighted < @as(i32, @intCast(filtered_count)) - 1) {
|
if (state.highlighted < @as(i32, @intCast(filtered_count)) - 1) {
|
||||||
state.highlighted += 1;
|
state.highlighted += 1;
|
||||||
// Scroll if needed
|
// Scroll if needed
|
||||||
|
|
@ -550,7 +487,6 @@ pub fn autocompleteRect(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Abrir dropdown si está cerrado
|
|
||||||
state.openDropdown();
|
state.openDropdown();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -568,17 +504,9 @@ pub fn autocompleteRect(
|
||||||
},
|
},
|
||||||
.backspace => {
|
.backspace => {
|
||||||
state.backspace();
|
state.backspace();
|
||||||
// Abrir dropdown después de borrar (el usuario está editando)
|
|
||||||
if (state.len >= config.min_chars) {
|
|
||||||
state.open = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.delete => {
|
.delete => {
|
||||||
state.delete();
|
state.delete();
|
||||||
// Abrir dropdown después de borrar
|
|
||||||
if (state.len >= config.min_chars) {
|
|
||||||
state.open = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.left => {
|
.left => {
|
||||||
if (!state.open) {
|
if (!state.open) {
|
||||||
|
|
@ -605,23 +533,17 @@ pub fn autocompleteRect(
|
||||||
if (text_in.len > 0) {
|
if (text_in.len > 0) {
|
||||||
state.insert(text_in);
|
state.insert(text_in);
|
||||||
result.text_changed = true;
|
result.text_changed = true;
|
||||||
// IMPORTANTE: Abrir dropdown inmediatamente después de insertar texto
|
|
||||||
// (no esperar al siguiente frame para detectar el cambio)
|
|
||||||
if (state.len >= config.min_chars) {
|
|
||||||
state.open = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw dropdown if open and has items
|
// Draw dropdown if open and has items
|
||||||
// OVERLAY: El dropdown se dibuja en la capa overlay para aparecer ENCIMA de otros widgets
|
|
||||||
if (state.open and filtered_count > 0) {
|
if (state.open and filtered_count > 0) {
|
||||||
const visible_items = @min(filtered_count, config.max_visible_items);
|
const visible_items = @min(filtered_count, config.max_visible_items);
|
||||||
const dropdown_h = visible_items * config.item_height;
|
const dropdown_h = visible_items * config.item_height;
|
||||||
const dropdown_y = bounds.y + @as(i32, @intCast(bounds.h));
|
const dropdown_y = bounds.y + @as(i32, @intCast(bounds.h));
|
||||||
|
|
||||||
// Dropdown background (overlay)
|
// Dropdown background
|
||||||
ctx.pushOverlayCommand(Command.rect(
|
ctx.pushCommand(Command.rect(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
dropdown_y,
|
dropdown_y,
|
||||||
bounds.w,
|
bounds.w,
|
||||||
|
|
@ -629,7 +551,7 @@ pub fn autocompleteRect(
|
||||||
colors.dropdown_bg,
|
colors.dropdown_bg,
|
||||||
));
|
));
|
||||||
|
|
||||||
ctx.pushOverlayCommand(Command.rectOutline(
|
ctx.pushCommand(Command.rectOutline(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
dropdown_y,
|
dropdown_y,
|
||||||
bounds.w,
|
bounds.w,
|
||||||
|
|
@ -670,7 +592,7 @@ pub fn autocompleteRect(
|
||||||
Style.Color.transparent;
|
Style.Color.transparent;
|
||||||
|
|
||||||
if (item_bg.a > 0) {
|
if (item_bg.a > 0) {
|
||||||
ctx.pushOverlayCommand(Command.rect(
|
ctx.pushCommand(Command.rect(
|
||||||
item_bounds.x + 1,
|
item_bounds.x + 1,
|
||||||
item_bounds.y,
|
item_bounds.y,
|
||||||
item_bounds.w - 2,
|
item_bounds.w - 2,
|
||||||
|
|
@ -679,11 +601,11 @@ pub fn autocompleteRect(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item text (overlay)
|
// Item text
|
||||||
const item_inner = item_bounds.shrink(config.padding);
|
const item_inner = item_bounds.shrink(config.padding);
|
||||||
const item_text_y = item_inner.y + @as(i32, @intCast((item_inner.h -| char_height) / 2));
|
const item_text_y = item_inner.y + @as(i32, @intCast((item_inner.h -| char_height) / 2));
|
||||||
|
|
||||||
ctx.pushOverlayCommand(Command.text(item_inner.x, item_text_y, options[i], Style.Color.rgb(220, 220, 220)));
|
ctx.pushCommand(Command.text(item_inner.x, item_text_y, options[i], Style.Color.rgb(220, 220, 220)));
|
||||||
|
|
||||||
// Handle click selection
|
// Handle click selection
|
||||||
if (item_clicked) {
|
if (item_clicked) {
|
||||||
|
|
@ -711,11 +633,11 @@ pub fn autocompleteRect(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (state.open and filtered_count == 0 and filter_text.len > 0) {
|
} else if (state.open and filtered_count == 0 and filter_text.len > 0) {
|
||||||
// Show "no matches" message (overlay)
|
// Show "no matches" message
|
||||||
const no_match_h: u32 = config.item_height;
|
const no_match_h: u32 = config.item_height;
|
||||||
const dropdown_y = bounds.y + @as(i32, @intCast(bounds.h));
|
const dropdown_y = bounds.y + @as(i32, @intCast(bounds.h));
|
||||||
|
|
||||||
ctx.pushOverlayCommand(Command.rect(
|
ctx.pushCommand(Command.rect(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
dropdown_y,
|
dropdown_y,
|
||||||
bounds.w,
|
bounds.w,
|
||||||
|
|
@ -723,7 +645,7 @@ pub fn autocompleteRect(
|
||||||
colors.dropdown_bg,
|
colors.dropdown_bg,
|
||||||
));
|
));
|
||||||
|
|
||||||
ctx.pushOverlayCommand(Command.rectOutline(
|
ctx.pushCommand(Command.rectOutline(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
dropdown_y,
|
dropdown_y,
|
||||||
bounds.w,
|
bounds.w,
|
||||||
|
|
@ -733,7 +655,7 @@ pub fn autocompleteRect(
|
||||||
|
|
||||||
const no_match_text = if (config.allow_custom) "Press Enter to use custom value" else "No matches found";
|
const no_match_text = if (config.allow_custom) "Press Enter to use custom value" else "No matches found";
|
||||||
const msg_y = dropdown_y + @as(i32, @intCast((no_match_h -| char_height) / 2));
|
const msg_y = dropdown_y + @as(i32, @intCast((no_match_h -| char_height) / 2));
|
||||||
ctx.pushOverlayCommand(Command.text(bounds.x + @as(i32, @intCast(config.padding)), msg_y, no_match_text, Style.Color.rgb(120, 120, 120)));
|
ctx.pushCommand(Command.text(bounds.x + @as(i32, @intCast(config.padding)), msg_y, no_match_text, Style.Color.rgb(120, 120, 120)));
|
||||||
|
|
||||||
// Close if clicked outside
|
// Close if clicked outside
|
||||||
if (ctx.input.mousePressed(.left) and !input_hovered) {
|
if (ctx.input.mousePressed(.left) and !input_hovered) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue