Skip to main content

base_ui/widget/widgets/
context_menu.rs

1use crate::graphics::Renderer;
2use crate::style::color::Color;
3use std::sync::Arc;
4use std::cell::RefCell;
5use log::debug;
6
7pub struct MenuItem {
8    text: String,
9    on_click: Option<Arc<RefCell<dyn FnMut() + 'static>>>,
10    background_color: Color,
11    text_color: Color,
12    hover_background_color: Color,
13    hover_text_color: Color,
14    is_hovered: bool,
15}
16
17impl MenuItem {
18    pub fn new(text: &str) -> Self {
19        debug!("Creating new menu item: {}", text);
20        Self {
21            text: text.to_string(),
22            on_click: None,
23            background_color: Color::new(0.9, 0.9, 0.9, 1.0),
24            text_color: Color::new(0.1, 0.1, 0.1, 1.0),
25            hover_background_color: Color::new(0.7, 0.7, 0.9, 1.0),
26            hover_text_color: Color::new(1.0, 1.0, 1.0, 1.0),
27            is_hovered: false,
28        }
29    }
30
31    pub fn set_on_click<F>(&mut self, callback: F) where F: FnMut() + 'static {
32        debug!("Setting click handler for menu item: {}", self.text);
33        self.on_click = Some(Arc::new(RefCell::new(callback)));
34    }
35}
36
37pub struct ContextMenu {
38    x: f32,
39    y: f32,
40    width: f32,
41    items: Vec<MenuItem>,
42    visible: bool,
43    item_height: f32,
44    padding: f32,
45    border_color: Color,
46    border_width: f32,
47}
48
49impl ContextMenu {
50    pub fn new() -> Self {
51        Self {
52            x: 0.0,
53            y: 0.0,
54            width: 200.0,
55            items: Vec::new(),
56            visible: false,
57            item_height: 30.0,
58            padding: 5.0,
59            border_color: Color::new(0.8, 0.8, 0.8, 1.0),
60            border_width: 1.0,
61        }
62    }
63
64    pub fn add_item(&mut self, item: MenuItem) {
65        debug!("Adding menu item: {}", item.text);
66        self.items.push(item);
67    }
68
69    pub fn show(&mut self, x: f32, y: f32) {
70        debug!("Showing context menu at ({}, {})", x, y);
71        self.x = x;
72        self.y = y;
73        self.visible = true;
74    }
75
76    pub fn hide(&mut self) {
77        debug!("Hiding context menu");
78        self.visible = false;
79    }
80
81    pub fn draw(&self, renderer: &mut Renderer, screen_width: f32, screen_height: f32) {
82        if !self.visible {
83            return;
84        }
85
86        let total_height = (self.items.len() as f32) * self.item_height;
87
88        // Draw border
89        renderer.draw_rect(
90            self.x - self.border_width,
91            self.y - self.border_width,
92            self.width + self.border_width * 2.0,
93            total_height + self.border_width * 2.0,
94            self.border_color.to_array()
95        );
96
97        // Draw menu items
98        for (i, item) in self.items.iter().enumerate() {
99            let item_y = self.y + (i as f32) * self.item_height;
100            let bg_color = if item.is_hovered {
101                item.hover_background_color
102            } else {
103                item.background_color
104            };
105            let text_color = if item.is_hovered { item.hover_text_color } else { item.text_color };
106
107            // Draw item background
108            renderer.draw_rect(self.x, item_y, self.width, self.item_height, bg_color.to_array());
109
110            // Draw item text
111            renderer
112                .text_renderer_mut()
113                .render_text(
114                    &item.text,
115                    self.x + self.padding,
116                    item_y + (self.item_height - 20.0) / 2.0,
117                    20.0,
118                    screen_width,
119                    screen_height,
120                    text_color.to_array()
121                );
122        }
123    }
124
125    pub fn handle_mouse_move(&mut self, x: f32, y: f32) {
126        if !self.visible {
127            return;
128        }
129
130        for (i, item) in self.items.iter_mut().enumerate() {
131            let item_y = self.y + (i as f32) * self.item_height;
132            item.is_hovered =
133                x >= self.x &&
134                x <= self.x + self.width &&
135                y >= item_y &&
136                y <= item_y + self.item_height;
137        }
138    }
139
140    pub fn handle_click(&mut self, x: f32, y: f32) -> bool {
141        if !self.visible {
142            return false;
143        }
144
145        let total_height = (self.items.len() as f32) * self.item_height;
146        if x < self.x || x > self.x + self.width || y < self.y || y > self.y + total_height {
147            debug!("Click outside menu bounds, hiding menu");
148            self.hide();
149            return false;
150        }
151
152        for (i, item) in self.items.iter_mut().enumerate() {
153            let item_y = self.y + (i as f32) * self.item_height;
154            if y >= item_y && y <= item_y + self.item_height {
155                debug!("Menu item clicked: {}", item.text);
156                if let Some(callback) = &item.on_click {
157                    callback.borrow_mut()();
158                }
159                self.hide();
160                return true;
161            }
162        }
163        false
164    }
165}