Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/core/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,12 @@ pub struct ContextMenuState {
pub selected: usize,
pub screen_x: u16,
pub screen_y: u16,
/// Height of the trigger element in line_height units (f32 so
/// backends with sub-cell row heights — e.g. GTK's tab row at
/// ceil(1.6 * line_height) — can place the menu flush against the
/// trigger). Non-zero opts into `ContextMenuPlacement::Below`
/// (#434); right-click flows leave this at 0.0 and use AnchorPoint.
pub trigger_height: f32,
}

/// One visible row in the flat explorer file-tree list.
Expand Down
8 changes: 4 additions & 4 deletions src/core/engine/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18381,7 +18381,7 @@ fn test_editor_action_menu_opens() {
let mut e = Engine::new();
e.buffer_mut().insert(0, "hello\n");
let gid = e.active_group;
e.open_editor_action_menu(gid, 0, 0);
e.open_editor_action_menu(gid, 0, 0, 1.0);
assert!(e.context_menu.is_some());
let cm = e.context_menu.as_ref().unwrap();
assert!(matches!(
Expand All @@ -18402,7 +18402,7 @@ fn test_editor_action_menu_close_others_disabled_with_one_tab() {
let mut e = Engine::new();
e.buffer_mut().insert(0, "hello\n");
let gid = e.active_group;
e.open_editor_action_menu(gid, 0, 0);
e.open_editor_action_menu(gid, 0, 0, 1.0);
let cm = e.context_menu.as_ref().unwrap();
// "Close Others" disabled with only 1 tab.
let close_others = cm
Expand All @@ -18422,7 +18422,7 @@ fn test_editor_action_menu_toggle_wrap() {
e.buffer_mut().insert(0, "hello\n");
let gid = e.active_group;
assert!(!e.settings.wrap);
e.open_editor_action_menu(gid, 0, 0);
e.open_editor_action_menu(gid, 0, 0, 1.0);
// Select "toggle_wrap" and confirm.
if let Some(ref mut cm) = e.context_menu {
if let Some(idx) = cm.items.iter().position(|i| i.action == "toggle_wrap") {
Expand All @@ -18442,7 +18442,7 @@ fn test_editor_action_menu_close_all() {
e.new_tab(None);
assert!(e.active_group().tabs.len() >= 2);
let gid = e.active_group;
e.open_editor_action_menu(gid, 0, 0);
e.open_editor_action_menu(gid, 0, 0, 1.0);
if let Some(ref mut cm) = e.context_menu {
if let Some(idx) = cm.items.iter().position(|i| i.action == "close_all") {
cm.selected = idx;
Expand Down
12 changes: 11 additions & 1 deletion src/core/engine/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,11 +691,18 @@ impl Engine {
selected,
screen_x: x,
screen_y: y,
trigger_height: 0.0,
});
}

/// Open the editor action menu ("..." button) for a tab bar group.
pub fn open_editor_action_menu(&mut self, group_id: GroupId, x: u16, y: u16) {
pub fn open_editor_action_menu(
&mut self,
group_id: GroupId,
x: u16,
y: u16,
trigger_height: f32,
) {
let group = match self.editor_groups.get(&group_id) {
Some(g) => g,
None => return,
Expand Down Expand Up @@ -775,6 +782,7 @@ impl Engine {
selected,
screen_x: x,
screen_y: y,
trigger_height,
});
}

Expand Down Expand Up @@ -957,6 +965,7 @@ impl Engine {
selected: 0,
screen_x: x,
screen_y: y,
trigger_height: 0.0,
});
}

Expand Down Expand Up @@ -1041,6 +1050,7 @@ impl Engine {
selected,
screen_x: x,
screen_y: y,
trigger_height: 0.0,
});
}

Expand Down
7 changes: 6 additions & 1 deletion src/gtk/click.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,12 @@ pub(super) fn handle_mouse_click(
ClickTarget::ActionMenuButton(group_id) => {
let col = (x / char_width.max(1.0)) as u16;
let row = (y / line_height.max(1.0)) as u16;
engine.open_editor_action_menu(group_id, col, row);
// #434: pass the trigger's exact height in line_height units so
// the menu sits flush against the button's bottom (no sub-cell
// gap). GTK's tab row is ceil(1.6 * line_height).
let trigger_h =
(render_mod::tab_row_height_px(line_height) / line_height.max(1.0)) as f32;
engine.open_editor_action_menu(group_id, col, row, trigger_h);
(None, None)
}
_ => (None, None),
Expand Down
15 changes: 12 additions & 3 deletions src/gtk/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1956,10 +1956,19 @@ pub(super) fn draw_context_menu_popup(

let anchor_x = cm.screen_col as f64 * char_width;
let anchor_y = cm.screen_row as f64 * line_height;
let trigger_height_px = cm.trigger_height as f64 * line_height;
// trigger_height is in line_height units (f32 — see ContextMenuState
// docs). GTK's tab row is 1.6× line_height, so the caller passes 1.6
// for the action button and the menu sits flush against the button
// bottom (#434).
let viewport = quadraui::Rect::new(0.0, 0.0, editor_width as f32, editor_height as f32);
let menu_layout = menu.layout(
anchor_x as f32,
anchor_y as f32,
let menu_layout = menu.layout_at(
quadraui::Rect::new(
anchor_x as f32,
anchor_y as f32,
0.0,
trigger_height_px as f32,
),
viewport,
menu_w as f32,
item_height,
Expand Down
13 changes: 12 additions & 1 deletion src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3559,6 +3559,11 @@ pub struct ContextMenuPanel {
pub selected_idx: usize,
pub screen_col: u16,
pub screen_row: u16,
/// Trigger element height in line_height units (f32; supports
/// sub-cell rows like GTK's 1.6× tab row). 0.0 = no trigger →
/// render at click coords (AnchorPoint). Non-zero opts into
/// `ContextMenuPlacement::Below` (#434).
pub trigger_height: f32,
}

/// A single rendered context menu item.
Expand Down Expand Up @@ -3607,12 +3612,17 @@ pub fn context_menu_panel_to_quadraui_context_menu(
.get(panel.selected_idx)
.copied()
.unwrap_or(0);
let placement = if panel.trigger_height > 0.0 {
quadraui::ContextMenuPlacement::Below
} else {
quadraui::ContextMenuPlacement::AnchorPoint
};
quadraui::ContextMenu {
id: quadraui::WidgetId::new("context_menu"),
items,
selected_idx,
bg: None,
placement: quadraui::ContextMenuPlacement::default(),
placement,
}
}

Expand Down Expand Up @@ -6215,6 +6225,7 @@ pub fn build_screen_layout(
selected_idx: cm.selected,
screen_col: cm.screen_x,
screen_row: cm.screen_y,
trigger_height: cm.trigger_height,
}),
find_replace: if engine.find_replace_open {
let match_info = if engine.search_matches.is_empty() {
Expand Down
8 changes: 6 additions & 2 deletions src/tui_main/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2524,7 +2524,10 @@ pub(super) fn handle_mouse(
}
TabBarClickTarget::ActionMenu => {
engine.active_group = group_id;
engine.open_editor_action_menu(group_id, col, row + 1);
// #434: pass tab-row height (1.0 row in TUI) so the
// engine drives Below placement; replaces the prior
// `row + 1` hack.
engine.open_editor_action_menu(group_id, col, row, 1.0);
}
_ => {
engine.handle_tab_bar_click(group_id, target);
Expand Down Expand Up @@ -2610,7 +2613,8 @@ pub(super) fn handle_mouse(
engine.open_editor_group(SplitDirection::Horizontal);
}
"tab:action_menu" => {
engine.open_editor_action_menu(engine.active_group, col, row + 1);
// #434: pass tab-row height (1.0 row in TUI).
engine.open_editor_action_menu(engine.active_group, col, row, 1.0);
}
_ => {}
}
Expand Down
10 changes: 7 additions & 3 deletions src/tui_main/render_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,9 +968,13 @@ pub(super) fn draw_frame(
.unwrap_or(0);
let outer_width = (max_label + max_shortcut + 6).clamp(20, 50) as f32;
let inner_width = (outer_width - 2.0).max(1.0);
let layout = menu.layout(
ctx_menu.screen_col as f32 + 1.0,
ctx_menu.screen_row as f32 + 1.0,
let layout = menu.layout_at(
quadraui::Rect::new(
ctx_menu.screen_col as f32 + 1.0,
ctx_menu.screen_row as f32 + 1.0,
0.0,
ctx_menu.trigger_height,
),
inner_viewport,
inner_width,
|_| quadraui::ContextMenuItemMeasure::new(1.0),
Expand Down