pierro 0.1.0

An immediate mode UI library for Rust
Documentation

use crate::{icons, vec2, Color, Id, Layout, LayoutInfo, Margin, PerAxis, Response, Size, UINodeParams, UI};

use super::{close_context_menu, h_line, horizontal, icon, is_context_menu_open, label, label_text_style, open_context_menu, render_context_menu, Theme};

#[derive(Default)]
struct MenuMemory {
    open_submenu_id: Option<Id>
}

pub fn menu_bar<F: FnOnce(&mut UI)>(ui: &mut UI, contents: F) {
    let theme = ui.style::<Theme>();
    let fill = theme.bg_dark;
    ui.with_node(
        UINodeParams::new(Size::fr(1.0), Size::fit())
            .with_fill(fill),
        |ui| {
            horizontal(ui, contents);
            h_line(ui);
    });
}

pub fn menu_bar_item<S: Into<String>, F: FnOnce(&mut UI)>(ui: &mut UI, label: S, contents: F) {
    let theme = ui.style::<Theme>();
    let margin = theme.widget_margin;
    let rounding = theme.widget_rounding;
    let open_fill = theme.bg_light;
    let text_style = label_text_style(ui);

    let response = ui.node(
        UINodeParams::new(Size::text(), Size::text())
            .with_fill(Color::TRANSPARENT)
            .with_margin(Margin::same(margin))
            .with_rounding(rounding)
            .with_text_style(text_style)
            .with_text(label)
            .sense_mouse()
    );

    if is_context_menu_open(ui, response.id) {
        ui.set_fill(response.node_ref, open_fill);
    }

    let parent_id = ui.get_parent_id(response.node_ref);
    let open_menu_id = ui.memory().get::<MenuMemory>(parent_id).open_submenu_id;
    
    render_context_menu(ui, response.id, contents);
    if open_menu_id != Some(response.id) && (response.mouse_pressed() || (response.hovered && open_menu_id.is_some())) {
        let button_rect = ui.memory().get::<LayoutInfo>(response.id).screen_rect;
        let position = button_rect.bl();
        open_context_menu(ui, response.id, position, PerAxis::splat(None));

        if let Some(open_menu_id) = open_menu_id {
            close_context_menu(ui, open_menu_id);
        }
        ui.memory().get::<MenuMemory>(parent_id).open_submenu_id = Some(response.id);
    }
    if open_menu_id == Some(response.id) && !is_context_menu_open(ui, response.id) {
        ui.memory().get::<MenuMemory>(parent_id).open_submenu_id = None;
    }
     
}

fn menu_button_common<S: Into<String>, F: FnOnce(&mut UI)>(ui: &mut UI, label_text: S, contents: F) -> Response {
    let theme = ui.style::<Theme>();
    let bg_hover = theme.hovered_color(theme.bg_light); 
    let margin = theme.widget_margin / 2.0;
    let rounding = theme.widget_rounding;

    let (response, _) = ui.with_node(
        UINodeParams::new(Size::fr(1.0), Size::fit())
            .with_layout(Layout::horizontal())
            .with_margin(Margin::same(margin))
            .with_rounding(rounding)
            .sense_mouse(),
        |ui| {
            label(ui, label_text);
            ui.node(UINodeParams::new(Size::fr(1.0), Size::fr(1.0)));
            contents(ui);
        }
    );

    if response.hovered {
        ui.set_fill(response.node_ref, bg_hover);
    }

    response
}

pub fn menu_button<S: Into<String>>(ui: &mut UI, label_text: S) -> Response {
    let response = menu_button_common(ui, label_text, |_| {});

    let parent_id = ui.get_parent_id(response.node_ref);
    let open_menu_id = ui.memory().get::<MenuMemory>(parent_id).open_submenu_id;
    
    if open_menu_id != Some(response.id) && response.hovered { 
        if let Some(open_menu_id) = open_menu_id {
            close_context_menu(ui, open_menu_id);
        }
    }

    response
}

pub fn menu_category<S: Into<String>, F: FnOnce(&mut UI)>(ui: &mut UI, label_text: S, contents: F) {
    let response = menu_button_common(ui, label_text, |ui| {
        icon(ui, icons::CARET_RIGHT);
    });

    let parent_id = ui.get_parent_id(response.node_ref);
    let open_menu_id = ui.memory().get::<MenuMemory>(parent_id).open_submenu_id;
    
    render_context_menu(ui, response.id, contents);
    if open_menu_id != Some(response.id) && response.hovered {
        let stroke_width = ui.style::<Theme>().widget_stroke_width;
        let button_rect = ui.memory().get::<LayoutInfo>(response.id).screen_rect;
        let parent_rect = ui.memory().get::<LayoutInfo>(parent_id).screen_rect;
        let position = vec2(parent_rect.right() - stroke_width, button_rect.top()); 
        open_context_menu(ui, response.id, position, PerAxis::splat(None));

        if let Some(open_menu_id) = open_menu_id {
            close_context_menu(ui, open_menu_id);
        }
        ui.memory().get::<MenuMemory>(parent_id).open_submenu_id = Some(response.id);
    }
    if open_menu_id == Some(response.id) && !is_context_menu_open(ui, response.id) {
        ui.memory().get::<MenuMemory>(parent_id).open_submenu_id = None;
    }

}