mostly correct line impl

This commit is contained in:
Alexander Mohr 2025-05-25 00:04:29 +02:00
parent 7f96889803
commit eafd962276
3 changed files with 100 additions and 40 deletions

View file

@ -99,6 +99,7 @@ This library is not available publicly yet as the interface is not stable enough
* Color files are not supported
* `line_wrap` is now called `line-wrap`
* Wofi has a C-API, that is not and won't be supported, but Worf can be used as a rust library.
* Most boolean options now need `true` or `false` as argument, as Worf is using the same struct for config and command line arguments and this is the only way to merge both data sources
### Dropped arguments / config values
* `mode`, use show

View file

@ -178,11 +178,10 @@ impl FromStr for KeyDetectionType {
#[derive(Debug, Deserialize, Serialize, Clone, Parser)]
#[clap(about = "Worf is a wofi clone written in rust, it aims to be a drop-in replacement")]
#[derive(Default)]
#[allow(clippy::struct_excessive_bools)] // it's fine for config
pub struct Config {
/// Forks the menu so you can close the terminal
#[clap(long = "fork")]
fork: bool,
#[clap(short = 'f', long = "fork")]
fork: Option<bool>,
/// Selects a config file to use
#[clap(short = 'c', long = "conf")]
@ -227,8 +226,7 @@ pub struct Config {
/// Set to 'false' to disable images, defaults to true
#[clap(short = 'I', long = "allow-images")]
#[serde(default = "default_true")]
allow_images: bool,
allow_images: Option<bool>,
/// If `true` pango markup is parsed
#[clap(short = 'm', long = "allow-markup")]
@ -285,7 +283,7 @@ pub struct Config {
no_actions: Option<bool>,
#[clap(short = 'L', long = "lines")]
lines: Option<u32>, // todo support this
lines: Option<i32>, // todo support this
#[clap(short = 'w', long = "columns")]
columns: Option<u32>,
@ -338,16 +336,19 @@ pub struct Config {
// todo re-add this
// #[serde(flatten)]
// key_custom: Option<HashMap<String, String>>,
global_coords: bool, // todo support this
global_coords: Option<bool>, // todo support this
/// If set to `true` the search field willOption<> be hidden.
#[clap(long = "hide-search")]
hide_search: bool,
dynamic_lines: bool, // todo support this
hide_search: Option<bool>,
#[clap(long = "dynamic-lines")]
dynamic_lines: bool, // todo support this
layer: Option<String>, // todo support this
copy_exec: Option<String>, // todo support this
single_click: bool, // todo support this
pre_display_exec: bool, // todo support this
#[clap(long = "single_click")]
single_click: Option<bool>, // todo support this
#[clap(long = "pre-display-exec")]
pre_display_exec: Option<bool>, // todo support this
/// Minimum score for a fuzzy search to be shown
#[clap(long = "fuzzy-min-score")]
@ -362,7 +363,7 @@ pub struct Config {
/// Display only icon in emoji mode
#[clap(long = "emoji-hide-string")]
emoji_hide_label: bool,
emoji_hide_label: Option<bool>,
#[clap(long = "keyboard-detection-type")]
key_detection_type: Option<KeyDetectionType>,
@ -371,7 +372,7 @@ pub struct Config {
impl Config {
#[must_use]
pub fn fork(&self) -> bool {
self.fork
self.fork.unwrap_or(false)
}
#[must_use]
@ -477,7 +478,7 @@ impl Config {
#[must_use]
pub fn allow_images(&self) -> bool {
self.allow_images
self.allow_images.unwrap_or(true)
}
#[must_use]
@ -521,7 +522,7 @@ impl Config {
#[must_use]
pub fn hide_search(&self) -> bool {
self.hide_search
self.hide_search.unwrap_or(false)
}
#[must_use]
@ -551,7 +552,7 @@ impl Config {
#[must_use]
pub fn emoji_hide_label(&self) -> bool {
self.emoji_hide_label
self.emoji_hide_label.unwrap_or(false)
}
#[must_use]
@ -560,15 +561,20 @@ impl Config {
.clone()
.unwrap_or(KeyDetectionType::Value)
}
#[must_use]
pub fn lines(&self) -> Option<i32> {
self.lines
}
}
fn default_false() -> bool {
false
}
fn default_true() -> bool {
true
}
// fn default_true() -> bool {
// true
// }
//
// // TODO

View file

@ -710,11 +710,10 @@ fn build_ui<T, P>(
let (_changed, provider_elements) = get_provider_elements.join().unwrap();
log::debug!("got items after {:?}", wait_for_items.elapsed());
let active_cfg = config.clone();
let animate_window = ui_elements.window.clone();
animate_window.connect_is_active_notify(move |w| {
window_show_resize(&active_cfg.clone(), w);
let cfg = config.clone();
let ui = Rc::clone(&ui_elements);
ui_elements.window.connect_is_active_notify(move |_| {
window_show_resize(&cfg.clone(), &ui);
});
build_ui_from_menu_items(&ui_elements, &meta, provider_elements);
@ -918,6 +917,7 @@ fn build_ui_from_menu_items<T: Clone + 'static + Send>(
&lock.len(),
start.elapsed()
);
ControlFlow::Break
} else {
ControlFlow::Continue
@ -1126,28 +1126,81 @@ fn sort_menu_items_by_score<T: Clone>(
}
}
fn window_show_resize(config: &Config, window: &ApplicationWindow) {
if let Some(surface) = window.surface() {
let display = surface.display();
let monitor = display.monitor_at_surface(&surface);
if let Some(monitor) = monitor {
let geometry = monitor.geometry();
let Some(target_width) = percent_or_absolute(&config.width(), geometry.width()) else {
return;
};
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 {
return;
};
let Some(target_height) = percent_or_absolute(&config.height(), geometry.height())
else {
return;
};
let display = surface.display();
let Some(monitor) = display.monitor_at_surface(&surface) else {
return;
};
let geometry = monitor.geometry();
// Calculate target width from config, return early if not set
let Some(target_width) = percent_or_absolute(&config.width(), geometry.width()) else {
log::error!("width is not set");
return;
};
// Calculate target height based on either lines or absolute height
let target_height = if let Some(lines) = config.lines() {
let (_, _, _, height_search) = ui.search.measure(Orientation::Vertical, 10_000);
let widget = {
let lock = ui.menu_rows.read().unwrap();
lock.iter().next().map(|(w, _)| w.clone())
};
if let Some(widget) = widget {
log::debug!(
"monitor geometry: {geometry:?}, target_height {target_height}, target_width {target_width}"
"widget, mapped: {}, realized {}, visible {}, has child {}, baseline {}, pref size {:#?}",
widget.is_mapped(),
widget.is_realized(),
widget.is_visible(),
widget.child().is_some(),
widget.allocated_baseline(),
widget.preferred_size()
);
if !widget.is_mapped() {
let c_clone = config.clone();
let ui_clone = Rc::clone(ui);
widget.connect_realize(move |_| {
window_show_resize(&c_clone, &Rc::clone(&ui_clone));
});
return;
}
window.set_width_request(target_width);
window.set_height_request(target_height);
let (_, nat, _, baseline) = widget.measure(Orientation::Vertical, 10_000);
log::debug!("natural height base {baseline}, nat {nat}");
// todo fix this eventually properly, so baseline is always set and not only in 85% of cases.
let height = if baseline > 0 {
baseline
} else if config.allow_images() {
i32::from(config.image_size()) // wild guess that image makes the most part of the row ...
} else {
nat // for my configuration way bigger than baseline
};
Some((height_search + height) * lines)
} else {
log::warn!("No widget for height calculation available");
Some(0)
}
} else if let Some(height) = percent_or_absolute(&config.height(), geometry.height()) {
Some(height)
} else {
log::error!("Widget is none");
Some(0)
};
// Apply the calculated size or log an error if height missing
if let Some(target_height) = target_height {
log::debug!("Setting width {target_width}, height {target_height}");
ui.window.set_height_request(target_height);
ui.window.set_width_request(target_width);
} else {
log::error!("height is not set");
}
}