GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Add config metadata and display file/key info in widget context menu
src/context.rs | 31 ++++++++++++++++++++++++-------
src/widget/core.rs | 21 +++++++++++++++------
src/widget/input/checkbox.rs | 12 ++++++++++++
src/widget/input/color_selector.rs | 6 ++++++
src/widget/input/dropdown.rs | 29 +++++++++++++++++++++++++++++
src/widget/input/font_selector.rs | 6 ++++++
src/widget/input/slider.rs | 6 ++++++
src/widget/input/spinbox.rs | 6 ++++++
src/widget/input/text_box.rs | 6 ++++++
9 files changed, 110 insertions(+), 13 deletions(-)
diff --git a/src/context.rs b/src/context.rs
index 3236f80..e8d5d3b 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -410,8 +410,8 @@ impl UiContext {
crate::widget::context_menu::is_visible()
}
- pub fn show_context_menu(&mut self, x: f32, y: f32, options: Vec<String>, target: *mut (dyn Element + 'static)) {
- crate::widget::context_menu::show(x, y, options, target);
+ pub fn show_context_menu(&mut self, x: f32, y: f32, options: Vec<String>, header_count: usize, target: *mut (dyn Element + 'static)) {
+ crate::widget::context_menu::show(x, y, options, header_count, target);
}
pub fn handle_right_click(&mut self, target: *mut (dyn Element + 'static), px: f32, py: f32) {
@@ -426,15 +426,32 @@ impl UiContext {
format!("[{}]", name)
};
- let options = if name == "TextBox" {
- vec![header, "Cut".to_string(), "Copy".to_string(), "Paste".to_string(), "Select All".to_string()]
+ let mut config_info = None;
+ if let Some(b) = unsafe { (*target).base() } {
+ if let (Some(ref file), Some(ref key)) = (&b.config_file, &b.config_key) {
+ config_info = Some((file.clone(), key.clone()));
+ }
+ }
+
+ let mut options = Vec::new();
+ options.push(header);
+ let mut header_count = 1;
+
+ if let Some((file, key)) = config_info {
+ options.push(format!("File: {}", file));
+ options.push(format!("Key: {}", key));
+ header_count = 3;
+ }
+
+ if name == "TextBox" {
+ options.extend(vec!["Cut".to_string(), "Copy".to_string(), "Paste".to_string(), "Select All".to_string()]);
} else {
- vec![header, "Copy".to_string(), "Paste".to_string()]
- };
+ options.extend(vec!["Copy".to_string(), "Paste".to_string()]);
+ }
let scroll_y = crate::widget::hover_animation::get_scroll_offset();
let adjusted_py = py - scroll_y;
- crate::widget::context_menu::show(px, adjusted_py, options, target);
+ crate::widget::context_menu::show(px, adjusted_py, options, header_count, target);
}
pub fn hide_context_menu(&mut self) {
diff --git a/src/widget/core.rs b/src/widget/core.rs
index 7b1cd27..45ec05e 100644
--- a/src/widget/core.rs
+++ b/src/widget/core.rs
@@ -457,6 +457,7 @@ pub mod context_menu {
pub options: Vec<String>,
pub hovered_item: Option<usize>,
pub target: Option<*mut (dyn Element + 'static)>,
+ pub header_count: usize,
}
impl ContextMenuState {
@@ -470,10 +471,11 @@ pub mod context_menu {
options: Vec::new(),
hovered_item: None,
target: None,
+ header_count: 0,
}
}
- pub fn show(&mut self, x: f32, y: f32, options: Vec<String>, target: *mut (dyn Element + 'static)) {
+ pub fn show(&mut self, x: f32, y: f32, options: Vec<String>, header_count: usize, target: *mut (dyn Element + 'static)) {
if target.is_null() {
return;
}
@@ -486,6 +488,7 @@ pub mod context_menu {
self.visible = true;
self.hovered_item = None;
self.target = Some(target);
+ self.header_count = header_count;
}
pub fn hide(&mut self) {
@@ -504,7 +507,7 @@ pub mod context_menu {
self.hovered_item = None;
if px >= self.x && px <= self.x + self.w && py >= self.y && py <= self.y + self.h {
let idx = ((py - self.y) / 24.0) as usize;
- if idx < self.options.len() && idx > 0 {
+ if idx < self.options.len() && idx >= self.header_count {
self.hovered_item = Some(idx);
}
}
@@ -524,7 +527,7 @@ pub mod context_menu {
if px >= self.x && px <= self.x + self.w && py >= self.y && py <= self.y + self.h {
let idx = ((py - self.y) / 24.0) as usize;
if idx < self.options.len() {
- if idx > 0 {
+ if idx >= self.header_count {
let opt = self.options[idx].clone();
if let Some(target_ptr) = self.target {
if !target_ptr.is_null() {
@@ -581,7 +584,7 @@ pub mod context_menu {
for (idx, opt) in self.options.iter().enumerate() {
let iy = self.y + idx as f32 * 24.0 + (24.0 - 12.0) / 2.0;
- let text_color = if idx == 0 {
+ let text_color = if idx < self.header_count {
[0x70, 0x70, 0x78]
} else if self.hovered_item == Some(idx) {
[0xff, 0xff, 0xff]
@@ -609,8 +612,8 @@ pub mod context_menu {
CONTEXT_MENU.with(|m| m.borrow().visible)
}
- pub fn show(x: f32, y: f32, options: Vec<String>, target: *mut (dyn Element + 'static)) {
- CONTEXT_MENU.with(|m| m.borrow_mut().show(x, y, options, target));
+ pub fn show(x: f32, y: f32, options: Vec<String>, header_count: usize, target: *mut (dyn Element + 'static)) {
+ CONTEXT_MENU.with(|m| m.borrow_mut().show(x, y, options, header_count, target));
}
pub fn hide() {
@@ -672,6 +675,8 @@ pub struct Widget {
pub focused: bool,
pub id: std::cell::Cell<Option<crate::widget::WidgetId>>,
pub dirty: bool,
+ pub config_file: Option<String>,
+ pub config_key: Option<String>,
}
impl Widget {
@@ -688,6 +693,8 @@ impl Widget {
focused: false,
id: std::cell::Cell::new(None),
dirty: true,
+ config_file: None,
+ config_key: None,
}
}
@@ -704,6 +711,8 @@ impl Widget {
focused: false,
id: std::cell::Cell::new(None),
dirty: true,
+ config_file: None,
+ config_key: None,
}
}
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index ffd9464..b79c926 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -23,6 +23,12 @@ impl Checkbox {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_checked(&mut self, checked: bool) {
self.checked = checked;
}
@@ -196,6 +202,12 @@ impl Toggle {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_label(&mut self, label: &str) {
self.base.label = Some(label.to_string());
}
diff --git a/src/widget/input/color_selector.rs b/src/widget/input/color_selector.rs
index baacbaf..d009036 100644
--- a/src/widget/input/color_selector.rs
+++ b/src/widget/input/color_selector.rs
@@ -93,6 +93,12 @@ impl ColorSelector {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn with_font_family(mut self, font_family: &str) -> Self {
self.font_family = font_family.to_string();
self
diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index 40cff69..ffdbcbd 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -39,6 +39,12 @@ impl Dropdown {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_label(&mut self, label: &str) {
self.base.label = Some(label.to_string());
}
@@ -437,5 +443,28 @@ mod tests {
assert_eq!(dd.selected, 1);
assert!(dd.take_change());
}
+
+ #[test]
+ fn test_dropdown_context_menu_with_config() {
+ let mut dummy = crate::context::UiContext::new();
+ let options = vec!["Option A".to_string(), "Option B".to_string()];
+ let mut dd = Dropdown::new(options, 0).with_config("path/to/config.json", "some_key");
+ dd.set_rect(10.0, 10.0, 100.0, 24.0);
+
+ assert!(!crate::widget::context_menu::is_visible());
+
+ // Right click dropdown
+ let handled = dd.mouse_input(MouseButton::Right, ElementState::Pressed, 50.0, 20.0, &mut dummy);
+ assert!(handled);
+
+ assert!(crate::widget::context_menu::is_visible());
+ let menu_options = crate::widget::context_menu::options();
+ assert!(menu_options.len() >= 3);
+ assert_eq!(menu_options[1], "File: path/to/config.json");
+ assert_eq!(menu_options[2], "Key: some_key");
+
+ crate::widget::context_menu::hide();
+ assert!(!crate::widget::context_menu::is_visible());
+ }
}
diff --git a/src/widget/input/font_selector.rs b/src/widget/input/font_selector.rs
index dd50b2a..b436d8a 100644
--- a/src/widget/input/font_selector.rs
+++ b/src/widget/input/font_selector.rs
@@ -27,6 +27,12 @@ impl FontSelector {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn take_change(&mut self) -> bool {
let changed = self.just_changed;
self.just_changed = false;
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index e8d23f6..40a71fe 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -46,6 +46,12 @@ impl Slider {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_label(&mut self, label: &str) {
self.base.label = Some(label.to_string());
}
diff --git a/src/widget/input/spinbox.rs b/src/widget/input/spinbox.rs
index 102e7f5..3d79fc5 100644
--- a/src/widget/input/spinbox.rs
+++ b/src/widget/input/spinbox.rs
@@ -46,6 +46,12 @@ impl Spinbox {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_label(&mut self, label: &str) {
self.base.label = Some(label.to_string());
}
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index f01b962..98c6799 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -191,6 +191,12 @@ impl TextBox {
self
}
+ pub fn with_config(mut self, file: &str, key: &str) -> Self {
+ self.base.config_file = Some(file.to_string());
+ self.base.config_key = Some(key.to_string());
+ self
+ }
+
pub fn set_label(&mut self, label: &str) {
self.base.label = Some(label.to_string());
}