Merge branch 'master' into popup2

This commit is contained in:
b-guild
2024-04-16 11:18:31 -07:00
committed by b-guild
13 changed files with 249 additions and 162 deletions
+6 -4
View File
@@ -42,6 +42,7 @@ use crate::{
message::MessageSender,
send_sync_message,
};
use fyrox::gui::menu::ContextMenuBuilder;
use std::{
cell::Cell,
fmt::{Debug, Formatter},
@@ -486,8 +487,8 @@ impl BlendSpaceFieldBuilder {
fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
let add_point;
let remove_point;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -500,8 +501,9 @@ impl BlendSpaceFieldBuilder {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
let field = BlendSpaceField {
+16 -12
View File
@@ -27,6 +27,7 @@ use crate::{
message::MessageSender,
scene::{commands::ChangeSelectionCommand, Selection},
};
use fyrox::gui::menu::ContextMenuBuilder;
use std::fmt::Debug;
use super::fetch_state_node_model_handle;
@@ -43,8 +44,8 @@ impl CanvasContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
let create_state;
let connect_all_nodes;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(WidgetBuilder::new().with_children([
{
create_state = create_menu_item("Create State", vec![], ctx);
@@ -56,8 +57,9 @@ impl CanvasContextMenu {
},
]))
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
@@ -145,8 +147,8 @@ impl NodeContextMenu {
let set_as_entry_state;
let enter_state;
let connect_to_all_nodes;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -176,8 +178,9 @@ impl NodeContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
@@ -319,15 +322,16 @@ pub struct TransitionContextMenu {
impl TransitionContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
let remove;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(WidgetBuilder::new().with_child({
remove = create_menu_item("Remove Transition", vec![], ctx);
remove
}))
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+38 -32
View File
@@ -33,6 +33,7 @@ use crate::{
message::MessageSender,
scene::{commands::ChangeSelectionCommand, Selection},
};
use fyrox::gui::menu::ContextMenuBuilder;
pub struct CanvasContextMenu {
create_play_animation: Handle<UiNode>,
@@ -50,32 +51,35 @@ impl CanvasContextMenu {
let create_blend_animations;
let create_blend_by_index;
let create_blend_space;
let menu = PopupBuilder::new(
WidgetBuilder::new()
.with_enabled(false) // Disabled by default.
.with_visibility(false),
)
.with_content(
StackPanelBuilder::new(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(
WidgetBuilder::new()
.with_child({
create_play_animation = create_menu_item("Play Animation", vec![], ctx);
create_play_animation
})
.with_child({
create_blend_animations = create_menu_item("Blend Animations", vec![], ctx);
create_blend_animations
})
.with_child({
create_blend_by_index = create_menu_item("Blend By Index", vec![], ctx);
create_blend_by_index
})
.with_child({
create_blend_space = create_menu_item("Blend Space", vec![], ctx);
create_blend_space
}),
.with_enabled(false) // Disabled by default.
.with_visibility(false),
)
.build(ctx),
.with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
create_play_animation = create_menu_item("Play Animation", vec![], ctx);
create_play_animation
})
.with_child({
create_blend_animations =
create_menu_item("Blend Animations", vec![], ctx);
create_blend_animations
})
.with_child({
create_blend_by_index = create_menu_item("Blend By Index", vec![], ctx);
create_blend_by_index
})
.with_child({
create_blend_space = create_menu_item("Blend Space", vec![], ctx);
create_blend_space
}),
)
.build(ctx),
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
@@ -187,8 +191,8 @@ impl NodeContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
let remove;
let set_as_root;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -201,8 +205,9 @@ impl NodeContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
@@ -279,15 +284,16 @@ pub struct ConnectionContextMenu {
impl ConnectionContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
let remove;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(WidgetBuilder::new().with_child({
remove = create_menu_item("Remove Connection", vec![], ctx);
remove
}))
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+6 -4
View File
@@ -24,6 +24,7 @@ use crate::fyrox::{
},
};
use crate::menu::create_menu_item;
use fyrox::gui::menu::ContextMenuBuilder;
use std::{
cell::{Cell, RefCell},
fmt::{Debug, Formatter},
@@ -65,8 +66,8 @@ impl ContextMenu {
fn new(ctx: &mut BuildContext) -> Self {
let add_signal;
let remove_signal;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -79,8 +80,9 @@ impl ContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+6 -4
View File
@@ -72,6 +72,7 @@ use crate::{
},
send_sync_message, utils,
};
use fyrox::gui::menu::ContextMenuBuilder;
use std::{
any::TypeId,
cmp::Ordering,
@@ -105,8 +106,8 @@ impl TrackContextMenu {
let set_target;
let rebind;
let duplicate;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -127,8 +128,9 @@ impl TrackContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+6 -4
View File
@@ -52,6 +52,7 @@ use crate::{
Message, Mode,
};
use fyrox::core::parking_lot::RwLock;
use fyrox::gui::menu::ContextMenuBuilder;
use std::{
ffi::OsStr,
path::{Path, PathBuf},
@@ -111,8 +112,8 @@ impl ContextMenu {
let copy_path;
let copy_file_name;
let dependencies;
let menu = PopupBuilder::new(WidgetBuilder::new())
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new()).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -153,8 +154,9 @@ impl ContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+6 -4
View File
@@ -28,6 +28,7 @@ use crate::{
gui::{make_dropdown_list_option, make_image_button_with_tooltip},
load_image, Brush, Color, DropdownListBuilder, Engine,
};
use fyrox::gui::menu::ContextMenuBuilder;
use std::sync::mpsc::Receiver;
struct ContextMenu {
@@ -39,8 +40,8 @@ struct ContextMenu {
impl ContextMenu {
pub fn new(ctx: &mut BuildContext) -> Self {
let copy;
let menu = PopupBuilder::new(WidgetBuilder::new())
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new()).with_content(
StackPanelBuilder::new(WidgetBuilder::new().with_child({
copy = MenuItemBuilder::new(WidgetBuilder::new())
.with_content(MenuItemContent::text("Copy"))
@@ -48,8 +49,9 @@ impl ContextMenu {
copy
}))
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
Self {
+6 -4
View File
@@ -50,6 +50,7 @@ use crate::{
scene::commands::material::{SetMaterialPropertyValueCommand, SetMaterialShaderCommand},
send_sync_message, Engine, Message,
};
use fyrox::gui::menu::ContextMenuBuilder;
use std::{path::Path, sync::Arc};
struct TextureContextMenu {
@@ -63,8 +64,8 @@ impl TextureContextMenu {
fn new(ctx: &mut BuildContext) -> Self {
let show_in_asset_browser;
let unassign;
let popup = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let popup = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -81,8 +82,9 @@ impl TextureContextMenu {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let popup = RcUiNodeHandle::new(popup, ctx.sender());
Self {
+6 -4
View File
@@ -1,3 +1,4 @@
use crate::menu::ContextMenuBuilder;
use crate::{
brush::Brush,
core::{
@@ -1310,8 +1311,8 @@ impl CurveEditorBuilder {
let key_properties;
let key_value;
let key_location;
let context_menu = PopupBuilder::new(WidgetBuilder::new())
.with_content(
let context_menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new()).with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_child({
@@ -1414,8 +1415,9 @@ impl CurveEditorBuilder {
}),
)
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let context_menu = RcUiNodeHandle::new(context_menu, ctx.sender());
if self.widget_builder.foreground.is_none() {
+43 -41
View File
@@ -6,10 +6,10 @@ use crate::{
},
draw::DrawingContext,
grid::{Column, GridBuilder, Row},
menu::{MenuItemBuilder, MenuItemContent, MenuItemMessage},
menu::{ContextMenu, ContextMenuBuilder, MenuItemBuilder, MenuItemContent, MenuItemMessage},
message::{MessageDirection, OsEvent, UiMessage},
messagebox::{MessageBoxBuilder, MessageBoxButtons, MessageBoxMessage, MessageBoxResult},
popup::{Placement, Popup, PopupBuilder, PopupMessage},
popup::{Placement, PopupBuilder, PopupMessage},
stack_panel::StackPanelBuilder,
text::{TextBuilder, TextMessage},
text_box::TextBoxBuilder,
@@ -38,7 +38,7 @@ pub struct FolderNameDialog {
#[derive(Clone, Visit, Reflect, Debug, ComponentProvider)]
pub struct ItemContextMenu {
#[component(include)]
pub popup: Popup,
pub base_menu: ContextMenu,
pub delete: Handle<UiNode>,
pub make_folder: Handle<UiNode>,
pub delete_message_box: Cell<Handle<UiNode>>,
@@ -49,13 +49,13 @@ impl Deref for ItemContextMenu {
type Target = Widget;
fn deref(&self) -> &Self::Target {
&self.popup.widget
&self.base_menu.popup.widget
}
}
impl DerefMut for ItemContextMenu {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.popup.widget
&mut self.base_menu.popup.widget
}
}
@@ -63,27 +63,27 @@ uuid_provider!(ItemContextMenu = "6a9d597f-6a9f-4bad-b569-4cff1a6deff7");
impl Control for ItemContextMenu {
fn on_remove(&self, sender: &Sender<UiMessage>) {
self.popup.on_remove(sender)
self.base_menu.on_remove(sender)
}
fn measure_override(&self, ui: &UserInterface, available_size: Vector2<f32>) -> Vector2<f32> {
self.popup.measure_override(ui, available_size)
self.base_menu.measure_override(ui, available_size)
}
fn arrange_override(&self, ui: &UserInterface, final_size: Vector2<f32>) -> Vector2<f32> {
self.popup.arrange_override(ui, final_size)
self.base_menu.arrange_override(ui, final_size)
}
fn draw(&self, drawing_context: &mut DrawingContext) {
self.popup.draw(drawing_context)
self.base_menu.draw(drawing_context)
}
fn update(&mut self, dt: f32, ui: &mut UserInterface) {
self.popup.update(dt, ui)
self.base_menu.update(dt, ui)
}
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.popup.handle_routed_message(ui, message);
self.base_menu.handle_routed_message(ui, message);
if let Some(PopupMessage::Placement(Placement::Cursor(_))) = message.data() {
if message.destination() == self.handle {
@@ -210,7 +210,7 @@ impl Control for ItemContextMenu {
}
fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
self.popup.preview_message(ui, message);
self.base_menu.preview_message(ui, message);
if let Some(MessageBoxMessage::Close(result)) = message.data() {
if message.destination() == self.delete_message_box.get() {
@@ -286,7 +286,7 @@ impl Control for ItemContextMenu {
ui: &mut UserInterface,
event: &OsEvent,
) {
self.popup.handle_os_event(self_handle, ui, event)
self.base_menu.handle_os_event(self_handle, ui, event)
}
}
@@ -294,38 +294,40 @@ impl ItemContextMenu {
pub fn build(ctx: &mut BuildContext) -> Handle<UiNode> {
let delete;
let make_folder;
let popup = PopupBuilder::new(
WidgetBuilder::new()
.with_preview_messages(true)
.with_visibility(false),
)
.with_content(
StackPanelBuilder::new(
let base_menu = ContextMenuBuilder::new(
PopupBuilder::new(
WidgetBuilder::new()
.with_width(120.0)
.with_child({
delete = MenuItemBuilder::new(
WidgetBuilder::new().with_margin(Thickness::uniform(2.0)),
)
.with_content(MenuItemContent::text("Delete"))
.build(ctx);
delete
})
.with_child({
make_folder = MenuItemBuilder::new(
WidgetBuilder::new().with_margin(Thickness::uniform(2.0)),
)
.with_content(MenuItemContent::text("Make Folder"))
.build(ctx);
make_folder
}),
.with_preview_messages(true)
.with_visibility(false),
)
.build(ctx),
.with_content(
StackPanelBuilder::new(
WidgetBuilder::new()
.with_width(120.0)
.with_child({
delete = MenuItemBuilder::new(
WidgetBuilder::new().with_margin(Thickness::uniform(2.0)),
)
.with_content(MenuItemContent::text("Delete"))
.build(ctx);
delete
})
.with_child({
make_folder = MenuItemBuilder::new(
WidgetBuilder::new().with_margin(Thickness::uniform(2.0)),
)
.with_content(MenuItemContent::text("Make Folder"))
.build(ctx);
make_folder
}),
)
.build(ctx),
),
)
.build_popup(ctx);
.build_context_menu(ctx);
let menu = Self {
popup,
base_menu,
delete,
make_folder,
delete_message_box: Default::default(),
@@ -336,7 +338,7 @@ impl ItemContextMenu {
}
fn item_path(&self, ui: &UserInterface) -> Option<PathBuf> {
ui.try_get(self.popup.placement.target())
ui.try_get(self.base_menu.popup.placement.target())
.and_then(|n| n.user_data_cloned::<PathBuf>())
}
}
+6 -4
View File
@@ -2,6 +2,7 @@
//! structure or enumeration recursively. It's primary usage is provide unified and simple way of introspection.
//! See [`Inspector`] docs for more info and usage examples.
use crate::menu::ContextMenuBuilder;
use crate::{
border::BorderBuilder,
check_box::CheckBoxBuilder,
@@ -878,8 +879,8 @@ impl InspectorContext {
});
let copy_value_as_string;
let menu = PopupBuilder::new(WidgetBuilder::new().with_visibility(false))
.with_content(
let menu = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_visibility(false)).with_content(
StackPanelBuilder::new(WidgetBuilder::new().with_child({
copy_value_as_string = MenuItemBuilder::new(WidgetBuilder::new())
.with_content(MenuItemContent::text("Copy Value as String"))
@@ -887,8 +888,9 @@ impl InspectorContext {
copy_value_as_string
}))
.build(ctx),
)
.build(ctx);
),
)
.build(ctx);
let menu = RcUiNodeHandle::new(menu, ctx.sender());
let stack_panel = StackPanelBuilder::new(
+102 -43
View File
@@ -375,6 +375,13 @@ pub struct MenuItem {
crate::define_widget_deref!(MenuItem);
impl MenuItem {
fn is_opened(&self, ui: &UserInterface) -> bool {
ui.try_get_of_type::<ContextMenu>(*self.items_panel)
.map_or(false, |items_panel| *items_panel.popup.is_open)
}
}
// MenuItem uses popup to show its content, popup can be top-most only if it is
// direct child of root canvas of UI. This fact adds some complications to search
// of parent menu - we can't just traverse the tree because popup is not a child
@@ -383,7 +390,7 @@ crate::define_widget_deref!(MenuItem);
fn find_menu(from: Handle<UiNode>, ui: &UserInterface) -> Handle<UiNode> {
let mut handle = from;
while handle.is_some() {
if let Some((_, panel)) = ui.find_component_up::<MenuItemsPanel>(handle) {
if let Some((_, panel)) = ui.find_component_up::<ContextMenu>(handle) {
// Continue search from parent menu item of popup.
handle = panel.parent_menu_item;
} else {
@@ -413,17 +420,14 @@ fn is_any_menu_item_contains_point(ui: &UserInterface, pt: Vector2<f32>) -> bool
fn close_menu_chain(from: Handle<UiNode>, ui: &UserInterface) {
let mut handle = from;
while handle.is_some() {
let popup_handle = ui.find_handle_up(handle, &mut |n| n.has_component::<Popup>());
let popup_handle = ui.find_handle_up(handle, &mut |n| n.has_component::<ContextMenu>());
if popup_handle.is_none() {
break;
}
ui.send_message(PopupMessage::close(
popup_handle,
MessageDirection::ToWidget,
));
if let Some(panel) = ui.try_get_of_type::<ContextMenu>(popup_handle) {
ui.send_message(PopupMessage::close(
popup_handle,
MessageDirection::ToWidget,
));
if let Some(panel) = ui.node(popup_handle).query_component::<MenuItemsPanel>() {
// Continue search from parent menu item of popup.
handle = panel.parent_menu_item;
} else {
@@ -509,6 +513,15 @@ impl Control for MenuItem {
));
}
}
WidgetMessage::MouseLeave => {
if !self.is_opened(ui) {
ui.send_message(MenuItemMessage::select(
self.handle,
MessageDirection::ToWidget,
false,
));
}
}
WidgetMessage::KeyDown(key_code) => {
if !message.handled() && *self.is_selected && *key_code == KeyCode::Enter {
ui.send_message(MenuItemMessage::click(
@@ -530,16 +543,19 @@ impl Control for MenuItem {
MenuItemMessage::Select(selected) => {
if *self.is_selected != *selected {
self.is_selected.set_value_and_mark_modified(*selected);
ui.send_message(DecoratorMessage::select(
*self.decorator,
MessageDirection::ToWidget,
*selected,
));
ui.send_message(WidgetMessage::focus(
self.handle,
MessageDirection::ToWidget,
));
if *selected {
ui.send_message(WidgetMessage::focus(
self.handle,
MessageDirection::ToWidget,
));
}
}
}
MenuItemMessage::Open => {
@@ -574,13 +590,23 @@ impl Control for MenuItem {
*self.items_panel,
MessageDirection::ToWidget,
));
if *deselect && *self.is_selected {
ui.send_message(DecoratorMessage::select(
*self.decorator,
ui.send_message(MenuItemMessage::select(
self.handle,
MessageDirection::ToWidget,
false,
));
}
// Recursively deselect everything in the sub-items container.
for &item in &*self.items_container.items {
ui.send_message(MenuItemMessage::close(
item,
MessageDirection::ToWidget,
true,
));
}
}
MenuItemMessage::Click => {}
MenuItemMessage::AddItem(item) => {
@@ -641,7 +667,7 @@ impl Control for MenuItem {
break;
} else {
let node = ui.node(handle);
if let Some(panel) = node.component_ref::<MenuItemsPanel>() {
if let Some(panel) = node.component_ref::<ContextMenu>() {
// Once we found popup in chain, we must extract handle
// of parent menu item to continue search.
handle = panel.parent_menu_item;
@@ -671,10 +697,7 @@ impl Control for MenuItem {
// Allow closing "orphaned" menus by clicking outside of them.
if let OsEvent::MouseInput { state, .. } = event {
if *state == ButtonState::Pressed {
if let Some(panel) = ui
.node(*self.items_panel)
.query_component::<MenuItemsPanel>()
{
if let Some(panel) = ui.node(*self.items_panel).query_component::<ContextMenu>() {
if *panel.popup.is_open {
// Ensure that cursor is outside of any menus.
if !is_any_menu_item_contains_point(ui, ui.cursor_position())
@@ -959,21 +982,19 @@ impl<'a, 'b> MenuItemBuilder<'a, 'b> {
}
let panel;
let popup = PopupBuilder::new(WidgetBuilder::new().with_min_size(Vector2::new(10.0, 10.0)))
.with_content({
panel = StackPanelBuilder::new(
WidgetBuilder::new().with_children(self.items.iter().cloned()),
)
.build(ctx);
panel
})
// We'll manually control if popup is either open or closed.
.stays_open(true)
.build_popup(ctx);
let items_panel = ctx.add_node(UiNode::new(MenuItemsPanel {
popup,
parent_menu_item: Default::default(),
}));
let items_panel = ContextMenuBuilder::new(
PopupBuilder::new(WidgetBuilder::new().with_min_size(Vector2::new(10.0, 10.0)))
.with_content({
panel = StackPanelBuilder::new(
WidgetBuilder::new().with_children(self.items.iter().cloned()),
)
.build(ctx);
panel
})
// We'll manually control if popup is either open or closed.
.stays_open(true),
)
.build(ctx);
let menu = MenuItem {
widget: self
@@ -997,7 +1018,7 @@ impl<'a, 'b> MenuItemBuilder<'a, 'b> {
let handle = ctx.add_node(UiNode::new(menu));
// "Link" popup with its parent menu item.
if let Some(popup) = ctx[items_panel].cast_mut::<MenuItemsPanel>() {
if let Some(popup) = ctx[items_panel].cast_mut::<ContextMenu>() {
popup.parent_menu_item = handle;
}
@@ -1009,13 +1030,15 @@ impl<'a, 'b> MenuItemBuilder<'a, 'b> {
/// an ability for keyboard navigation.
#[derive(Default, Clone, Debug, Visit, Reflect, TypeUuidProvider, ComponentProvider)]
#[type_uuid(id = "ad8e9e76-c213-4232-9bab-80ebcabd69fa")]
pub struct MenuItemsPanel {
pub struct ContextMenu {
/// Inner popup widget of the context menu.
#[component(include)]
popup: Popup,
parent_menu_item: Handle<UiNode>,
pub popup: Popup,
/// Parent menu item of the context menu. Allows you to build chained context menus.
pub parent_menu_item: Handle<UiNode>,
}
impl Deref for MenuItemsPanel {
impl Deref for ContextMenu {
type Target = Widget;
fn deref(&self) -> &Self::Target {
@@ -1023,13 +1046,13 @@ impl Deref for MenuItemsPanel {
}
}
impl DerefMut for MenuItemsPanel {
impl DerefMut for ContextMenu {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.popup.widget
}
}
impl Control for MenuItemsPanel {
impl Control for ContextMenu {
fn on_remove(&self, sender: &Sender<UiMessage>) {
self.popup.on_remove(sender)
}
@@ -1087,6 +1110,42 @@ impl Control for MenuItemsPanel {
}
}
/// Creates [`ContextMenu`] widgets.
pub struct ContextMenuBuilder {
popup_builder: PopupBuilder,
parent_menu_item: Handle<UiNode>,
}
impl ContextMenuBuilder {
/// Creates new builder instance using an instance of the [`PopupBuilder`].
pub fn new(popup_builder: PopupBuilder) -> Self {
Self {
popup_builder,
parent_menu_item: Default::default(),
}
}
/// Sets the desired parent menu item.
pub fn with_parent_menu_item(mut self, parent_menu_item: Handle<UiNode>) -> Self {
self.parent_menu_item = parent_menu_item;
self
}
/// Finishes context menu building.
pub fn build_context_menu(self, ctx: &mut BuildContext) -> ContextMenu {
ContextMenu {
popup: self.popup_builder.build_popup(ctx),
parent_menu_item: self.parent_menu_item,
}
}
/// Finishes context menu building and adds it to the user interface.
pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
let context_menu = self.build_context_menu(ctx);
ctx.add_node(UiNode::new(context_menu))
}
}
fn keyboard_navigation(
ui: &UserInterface,
key_code: KeyCode,
+2 -2
View File
@@ -21,7 +21,7 @@ use crate::{
inspector::Inspector,
key::{HotKeyEditor, KeyBindingEditor},
list_view::{ListView, ListViewItem},
menu::{Menu, MenuItem, MenuItemsPanel},
menu::{ContextMenu, Menu, MenuItem},
messagebox::MessageBox,
nine_patch::NinePatch,
numeric::NumericUpDown,
@@ -95,7 +95,7 @@ impl WidgetConstructorContainer {
container.add::<ListView>();
container.add::<Menu>();
container.add::<MenuItem>();
container.add::<MenuItemsPanel>();
container.add::<ContextMenu>();
container.add::<MessageBox>();
container.add::<NinePatch>();