rvimage 0.6.7

A remote image viewer with a labeling tool
Documentation
use std::{
    fmt::{Debug, Display},
    ops::RangeInclusive,
    str::FromStr,
};

use egui::{
    FontSelection, Response, TextBuffer, TextEdit, Ui,
    text::{CCursor, CCursorRange},
};
use tracing::warn;

fn ui_with_deactivated_tools(
    are_tools_active: &mut bool,
    mut f_ui: impl FnMut() -> Response,
    event_activate: impl Fn(&Response) -> bool,
    event_deactivate: impl Fn(&Response) -> bool,
) -> Response {
    let response = f_ui();
    *are_tools_active = if event_deactivate(&response) {
        false
    } else if event_activate(&response) {
        true
    } else {
        *are_tools_active
    };
    response
}

pub fn ui_with_deactivated_tools_on_keys(
    are_tools_active: &mut bool,
    f_ui: impl FnMut() -> Response,
) -> Response {
    ui_with_deactivated_tools(
        are_tools_active,
        f_ui,
        |response| response.lost_focus(),
        |response| response.gained_focus(),
    )
}
pub fn ui_with_deactivated_tools_on_hover(
    are_tools_active: &mut bool,
    f_ui: impl FnMut() -> Response,
) -> Response {
    ui_with_deactivated_tools(
        are_tools_active,
        f_ui,
        |response| !response.hovered(),
        |response| response.hovered(),
    )
}

pub fn text_edit_with_deactivated_tools<S: TextBuffer>(
    text: &mut S,
    are_tools_active: &mut bool,
    mut f_ui: impl FnMut(&mut S) -> Response,
) -> Response {
    ui_with_deactivated_tools_on_keys(are_tools_active, || f_ui(text))
}
pub fn text_edit_multiline(
    ui: &mut Ui,
    text: &mut String,
    are_tools_active: &mut bool,
) -> Response {
    text_edit_with_deactivated_tools(text, are_tools_active, |text| {
        TextEdit::multiline(text)
            .font(FontSelection::Style(egui::TextStyle::Monospace))
            .show(ui)
            .response
            .response
    })
}
pub fn text_edit_singleline(
    ui: &mut Ui,
    text: &mut String,
    are_tools_active: &mut bool,
) -> Response {
    text_edit_with_deactivated_tools(text, are_tools_active, |text| {
        let mut textedit_output = TextEdit::singleline(text)
            .font(FontSelection::Style(egui::TextStyle::Monospace))
            .show(ui);
        if textedit_output.response.clicked() {
            textedit_output
                .state
                .cursor
                .set_char_range(Some(CCursorRange::two(
                    CCursor::new(0),
                    CCursor::new(text.len()),
                )));
            textedit_output
                .state
                .store(ui.ctx(), textedit_output.response.id);
        }
        textedit_output.response.response
    })
}
pub fn slider<Num>(
    ui: &mut Ui,
    are_tools_active: &mut bool,
    value: &mut Num,
    range: RangeInclusive<Num>,
    text: &str,
) -> Response
where
    Num: egui::emath::Numeric,
{
    ui_with_deactivated_tools_on_keys(are_tools_active, || {
        ui.add(egui::Slider::new(value, range.clone()).text(text))
    })
}

pub fn process_number<T>(
    ui: &mut Ui,
    are_tools_active: &mut bool,
    label: &str,
    buffer: &mut String,
) -> (bool, Option<T>)
where
    T: Display + FromStr,
    <T as FromStr>::Err: Debug,
{
    let new_val = text_edit_singleline(ui, buffer, are_tools_active).on_hover_text(label);
    if new_val.changed() {
        match buffer.parse::<T>() {
            Ok(val) => (true, Some(val)),
            Err(e) => {
                if !buffer.is_empty() {
                    warn!("could not parse '{buffer}' as number due to {e:?}");
                    (false, None)
                } else {
                    (true, None)
                }
            }
        }
    } else {
        (false, None)
    }
}
pub fn button_triggerable_number<T>(
    ui: &mut Ui,
    buffer: &mut String,
    are_tools_active: &mut bool,
    btn_label: &str,
    tool_tip: &str,
    warning_tool_tip_btn: Option<&str>,
) -> Option<T>
where
    T: Display + FromStr,
    <T as FromStr>::Err: Debug,
{
    let _ = process_number::<T>(ui, are_tools_active, tool_tip, buffer);
    let btn = ui.button(btn_label);
    let clicked = if let Some(warning) = warning_tool_tip_btn {
        btn.on_hover_text(warning).double_clicked()
    } else {
        btn.clicked()
    };
    if clicked {
        buffer
            .parse::<T>()
            .inspect_err(|e| tracing::warn!("could not parse '{buffer}' as number due to {e:?}"))
            .ok()
    } else {
        None
    }
}