support dynamic rows fixes #42

This commit is contained in:
Alexander Mohr 2025-06-01 00:39:15 +02:00
parent 0f89e367a8
commit 9e2f4d4eac
2 changed files with 83 additions and 130 deletions

View file

@ -368,7 +368,7 @@ pub struct Config {
hide_search: Option<bool>,
#[clap(long = "dynamic-lines")]
dynamic_lines: Option<bool>, // todo support this
dynamic_lines: Option<bool>,
#[clap(long = "layer")]
layer: Option<Layer>,
@ -635,6 +635,11 @@ impl Config {
pub fn layer(&self) -> Layer {
self.layer.clone().unwrap_or(Layer::Top)
}
#[must_use]
pub fn dynamic_lines(&self) -> bool {
self.dynamic_lines.unwrap_or(false)
}
}
fn default_false() -> bool {
@ -645,83 +650,6 @@ fn default_false() -> bool {
// true
// }
//
// // TODO
// // GtkOrientation orientation = config_get_mnemonic(config, "orientation", "vertical", 2, "vertical", "horizontal");
// // outer_orientation = config_get_mnemonic(cstoonfig, "orientation", "vertical", 2, "horizontal", "vertical");
// // GtkAlign halign = config_get_mnemonic(config, "halign", "fill", 4, "fill", "start", "end", "center");
// // content_halign = config_get_mnemonic(config, "content_halign", "fill", 4, "fill", "start", "end", "center");
// // char* default_valign = "start";
// // if(outer_orientation == GTK_ORIENTATION_HORIZONTAL) {
// // default_valign = "center";
// // }
// // GtkAlign valign = config_get_mnemonic(config, "valign", default_valign, 4, "fill", "start", "end", "center");
// // char* prompt = config_get(config, "prompt", mode);
// // uint64_t filter_rate = strtol(config_get(config, "filter_rate", "100"), NULL, 10);
// // allow_markup = strcmp(config_get(config, "allow_markup", "false"), "true") == 0;
// // image_size = strtol(config_get(config, "image_size", "32"), NULL, 10);
// // cache_file = map_get(config, "cache_file");
// // config_dir = map_get(config, "config_dir");
// // terminal = map_get(config, "term");
// // exec_search = strcmp(config_get(config, "exec_search", "false"), "true") == 0;
// // bool hide_scroll = strcmp(config_get(config, "hide_scroll", "false"), "true") == 0;
// // matching = config_get_mnemonic(config, "matching", "contains", 3, "contains", "multi-contains", "fuzzy");
// // insensitive = strcmp(config_get(config, "insensitive", "false"), "true") == 0;
// // parse_search = strcmp(config_get(config, "parse_search", "false"), "true") == 0;
// // location = config_get_mnemonic(config, "location", "center", 18,
// // "center", "top_left", "top", "top_right", "right", "bottom_right", "bottom", "bottom_left", "left",
// // "0", "1", "2", "3", "4", "5", "6", "7", "8");
// // no_actions = strcmp(config_get(config, "no_actions", "false"), "true") == 0;
// // lines = strtol(config_get(config, "lines", "0"), NULL, 10);
// // max_lines = lines;
// // columns = strtol(config_get(config, "columns", "1"), NULL, 10);
// // sort_order = config_get_mnemonic(config, "sort_order", "default", 2, "default", "alphabetical");
// // bool global_coords = strcmp(config_get(config, "global_coords", "false"), "true") == 0;
// // hide_search = strcmp(config_get(config, "hide_search", "false"), "true") == 0;
// // char* search = map_get(config, "search");
// // dynamic_lines = strcmp(config_get(config, "dynamic_lines", "false"), "true") == 0;
// // char* monitor = map_get(config, "monitor");
// // char* layer = config_get(config, "layer", "top");
// // copy_exec = config_get(config, "copy_exec", "wl-copy");
// // pre_display_cmd = map_get(config, "pre_display_cmd");
// // pre_display_exec = strcmp(config_get(config, "pre_display_exec", "false"), "true") == 0;
// // single_click = strcmp(config_get(config, "single_click", "false"), "true") == 0;
// //
// // keys = map_init_void();
// // mods = map_init_void();
// //
// // map_put_void(mods, "Shift", &shift_mask);
// // map_put_void(mods, "Ctrl", &ctrl_mask);
// // map_put_void(mods, "Alt", &alt_mask);
// //
// // key_default = "Up";
// // char* key_up = (i == 0) ? "Up" : config_get(config, "key_up", key_default);
// // key_default = "Down";
// // char* key_down = (i == 0) ? key_default : config_get(config, "key_down", key_default);
// // key_default = "Left";
// // char* key_left = (i == 0) ? key_default : config_get(config, "key_left", key_default);
// // key_default = "Right";
// // char* key_right = (i == 0) ? key_default : config_get(config, "key_right", key_default);
// // key_default = "Tab";
// // char* key_forward = (i == 0) ? key_default : config_get(config, "key_forward", key_default);
// // key_default = "Shift-ISO_Left_Tab";
// // char* key_backward = (i == 0) ? key_default : config_get(config, "key_backward", key_default);
// // key_default = "Return";
// // char* key_submit = (i == 0) ? key_default : config_get(config, "key_submit", key_default);
// // key_default = "Escape";
// // char* key_exit = (i == 0) ? key_default : config_get(config, "key_exit", key_default);
// // key_default = "Page_Up";
// // char* key_pgup = (i == 0) ? key_default : config_get(config, "key_pgup", key_default);
// // key_default = "Page_Down";
// // char* key_pgdn = (i == 0) ? key_default : config_get(config, "key_pgdn", key_default);
// // key_default = "";
// // char* key_expand = (i == 0) ? key_default: config_get(config, "key_expand", key_default);
// // key_default = "";
// // char* key_hide_search = (i == 0) ? key_default: config_get(config, "key_hide_search", key_default);
// // key_default = "Ctrl-c";
// // char* key_copy = (i == 0) ? key_default : config_get(config, "key_copy", key_default);
// }
#[must_use]
pub fn parse_args() -> Config {
Config::parse()

View file

@ -988,7 +988,16 @@ fn handle_key_press<T: Clone + 'static + Send>(
&meta.config,
meta.search_ignored_words.as_ref(),
);
select_first_visible_child(&*lock, &ui.main_box);
drop(lock);
if meta.config.dynamic_lines() {
ui.window.set_height_request(calculate_row_height(
ui,
visible_row_count(ui),
&meta.config,
));
}
};
let update_view_from_provider = |query: &String| {
@ -1151,7 +1160,6 @@ fn sort_menu_items_by_score<T: Clone>(
}
}
#[allow(clippy::cast_possible_truncation)] // does not matter for calculating height
fn window_show_resize<T: Clone + 'static>(config: &Config, ui: &Rc<UiElements<T>>) {
// Get the surface and associated monitor geometry
let Some(surface) = ui.window.surface() else {
@ -1171,57 +1179,9 @@ fn window_show_resize<T: Clone + 'static>(config: &Config, ui: &Rc<UiElements<T>
};
let target_height = if let Some(lines) = config.lines() {
let (_, _, _, height_search) = ui.search.measure(Orientation::Vertical, 10_000);
let (height_box, _, _, _) = ui.custom_key_box.measure(Orientation::Vertical, 10_000);
let (_, scroll_height, _, _) = ui.scroll.measure(Orientation::Vertical, 10_000);
let (_, window_height, _, _) = ui.window.measure(Orientation::Vertical, 10_000);
let height = {
let lock = ui.menu_rows.read().unwrap();
lock.iter()
.find_map(|(fb, _)| {
let (_, _, _, baseline) = fb.measure(Orientation::Vertical, 10_000);
if baseline > 0 {
let factor = if lines > 1 {
1.4 // todo find a better way to do this
// most likely it will not work with all styles
} else {
1.0
};
if config.allow_images() && baseline < i32::from(config.image_size()) {
Some(i32::from(config.image_size()))
} else {
Some((f64::from(baseline) * factor) as i32)
}
} else {
None
}
})
.or_else(|| {
lock.iter().find_map(|(fb, _)| {
let (_, nat, _, _) = fb.measure(Orientation::Vertical, 10_000);
if nat > 0 { Some(nat) } else { None }
})
})
};
log::debug!(
"heights: scroll {scroll_height}, window {window_height}, keys {height_box}, height {height:?}"
);
if let Some(height) = height {
Some(
height_box
+ scroll_height
+ height_search
+ height * lines
+ config.lines_additional_space(),
)
} else {
log::warn!("No widget for height calculation available");
Some(0)
}
Some(calculate_row_height(ui, lines, config))
} else if config.dynamic_lines() {
Some(calculate_row_height(ui, visible_row_count(ui), config))
} else if let Some(height) = percent_or_absolute(&config.height(), geometry.height()) {
Some(height)
} else {
@ -1238,10 +1198,75 @@ fn window_show_resize<T: Clone + 'static>(config: &Config, ui: &Rc<UiElements<T>
}
}
#[allow(clippy::cast_possible_truncation)] // does not matter for calculating height
fn calculate_row_height<T: Clone + 'static>(
ui: &UiElements<T>,
lines: i32,
config: &Config,
) -> i32 {
const MEAS_SIZE: i32 = 10_000;
let (_, _, _, height_search) = ui.search.measure(Orientation::Vertical, MEAS_SIZE);
let (height_box, _, _, _) = ui.custom_key_box.measure(Orientation::Vertical, MEAS_SIZE);
let (_, scroll_height, _, _) = ui.scroll.measure(Orientation::Vertical, MEAS_SIZE);
let (_, window_height, _, _) = ui.window.measure(Orientation::Vertical, MEAS_SIZE);
let height = {
let lock = ui.menu_rows.read().unwrap();
lock.iter()
.find_map(|(fb, _)| {
let (_, _, _, baseline) = fb.measure(Orientation::Vertical, MEAS_SIZE);
if baseline > 0 {
let factor = if lines > 1 {
1.4 // todo find a better way to do this
// most likely it will not work with all styles
} else {
1.0
};
if config.allow_images() && baseline < i32::from(config.image_size()) {
Some(i32::from(config.image_size()))
} else {
Some((f64::from(baseline) * factor) as i32)
}
} else {
None
}
})
.or_else(|| {
lock.iter().find_map(|(fb, _)| {
let (_, nat, _, _) = fb.measure(Orientation::Vertical, MEAS_SIZE);
if nat > 0 { Some(nat) } else { None }
})
})
};
log::debug!(
"heights: scroll {scroll_height}, window {window_height}, keys {height_box}, height {height:?}"
);
height_box
+ scroll_height
+ height_search
+ height.map_or(0, |h| h * lines)
+ config.lines_additional_space()
}
fn close_gui(app: &Application) {
app.quit();
}
fn visible_row_count<T: Clone + 'static>(ui: &UiElements<T>) -> i32 {
i32::try_from(
ui.menu_rows
.read()
.unwrap()
.iter()
.filter(|(_, menu)| menu.visible)
.count(),
)
.unwrap_or(i32::MAX)
}
fn handle_selected_item<T>(
ui: &Rc<UiElements<T>>,
meta: Rc<MetaData<T>>,